This article mainly introduces how to use AmplifyJS components to program with JavaScript. The subscription function provided by AmplifyJS is very powerful. For more information, see
Functions of event Distribution
When adding various interactive functions to a page, we are familiar with the simplest way to bind events to page elements, and then perform the actions we want in the event processing function. Code like this:
element.onclick = function(event){ // Do anything.};
If the action we want to do is not complex, we can put the code of the actual logic function here. If you need to modify it in the future, go to the location of the event processing function.
Further, we may split some of the logic functions into one function for proper code reuse:
element.onclick = function(event){ // Other code here. doSomethingElse();};
The doSomethingElse function may be used elsewhere, so it will be split. In addition, there may be a function like setting coordinates (assuming the function is called setPosition), you also need to use information such as the pointer location provided by the event object in the browser:
element.onclick = function(event){ // Other code here. doSomethingElse(); setPosition(event.clientX, event.clientY);};
It is not recommended to pass the event object directly to setPosition. This is because it is a good practice to distinguish between logical functions and event listening. Only the event handler function is exposed to the browser event object, which helps reduce code coupling and facilitate independent testing and maintenance.
So what if more and more functions become more and more complex? If the previous practice is followed, it may look like this:
element.onclick = function(event){ doMission1(); doMission2(event.clientX, event.clientY); doMission3(); // ... doMissionXX();};
Although this is not a problem, you can consider a more elegant way of writing at this time:
element.onclick = function(event){ amplify.publish( "aya:clicked", { x: event.clientX, y: event.clientY });};
This form is event distribution. Please note that the event here does not refer to the browser native event (event object), but a Custom event at the logic level. The above aya: clicked is a random write (really ?) The name of the Custom Event.
Obviously, this is not over yet. To complete the complex functions, we also need to associate custom events with the tasks to be done:
amplify.subscribe( "aya:clicked", doMission1);// ...amplify.subscribe( "aya:clicked", doMission2);// ...
Does it look like it's back? Yes, but this is useful. On the one hand, the browser native event listening is separated and solidified. If the logic functions change in the future, for example, to reduce the number of functions, you only need to delete the associated code of the Custom Event, you do not need to worry about native events. On the other hand, the adjustment of logical functions becomes more flexible. You can add functions in any code location through subscribe, and you can perform classification management (Custom Event names) by yourself ).
Simply put, event distribution reduces the coupling between code modules by adding a layer of Custom Event redundancy (when only simple logic functions are available, you will think it is redundant, this makes logical functions clearer and more organized for subsequent maintenance.
Wait, what does amplify, which has exists several times before, do?
Nice, it's time to introduce this.
AmplifyJS
Event distribution requires some methods. One of the design modes for implementing event distribution is Publish/Subscribe ).
AmplifyJS is a simple JavaScript library that provides three functions: Ajax request, data storage, and publishing/subscription (each item can be used independently ). Among them, publishing/subscription is the core function, and the corresponding name is amplify. core.
Amplify. core is a concise and clear implementation of the release/subscription design model, with over 100 lines of comments added. After reading the source code of amplify, you can better understand how to implement a publishing/subscription design mode.
Code Overview
The overall structure of amplify. core source code is as follows:
(function( global, undefined ) {var slice = [].slice, subscriptions = {};var amplify = global.amplify = { publish: function( topic ) { // ... }, subscribe: function( topic, context, callback, priority ) { // ... }, unsubscribe: function( topic, context, callback ) { // ... }};}( this ) );
As you can see, amplify defines a global variable named amplify (as a global attribute). It has three methods: publish, subscribe, and unsubscribe. In addition, as a local variable, subscriptions stores all the Custom Event names and their associated functions involved in the publishing/subscription mode.
Publish
Publish is a release. It requires that a topic be specified, that is, the Custom Event name (or topic). After the call, all functions associated with a topic are called in sequence:
publish: function( topic ) { // [1] if ( typeof topic !== "string" ) { throw new Error( "You must provide a valid topic to publish." ); } // [2] var args = slice.call( arguments, 1 ), topicSubscriptions, subscription, length, i = 0, ret; if ( !subscriptions[ topic ] ) { return true; } // [3] topicSubscriptions = subscriptions[ topic ].slice(); for ( length = topicSubscriptions.length; i < length; i++ ) { subscription = topicSubscriptions[ i ]; ret = subscription.callback.apply( subscription.context, args ); if ( ret === false ) { break; } } return ret !== false;},
[1], the parameter topic must be a string; otherwise, an error is thrown.
[2], args Retrieves all the parameters passed to the publish function except the topic and saves them as arrays. If the corresponding topic is not found in subscriptions, return directly.
[3]. topicSubscriptions is used as an array to obtain all associated elements under a topic. Each element includes the callback and context parts. Then, traverse the element, call the callback of each associated element, and bring the context of the element and the preceding extra parameter args. If the callback function of any correlated element returns false, the system stops running other elements and returns false.
Subscribe
Subscription, as defined in the term itself (just like subscribing to a magazine or something), is a step to establish the association between topics and callback. In particular, amplify also adds the priority (priority) concept here. The smaller the priority value, the higher the priority. The default value is 10. The higher-priority callback will be called in publish. The principle of this order can be seen from the source code of the previous publish. In fact, all the associated elements of a topic are arranged in order of priority from high to low.
subscribe: function( topic, context, callback, priority ) { if ( typeof topic !== "string" ) { throw new Error( "You must provide a valid topic to create a subscription." ); } // [1] if ( arguments.length === 3 && typeof callback === "number" ) { priority = callback; callback = context; context = null; } if ( arguments.length === 2 ) { callback = context; context = null; } priority = priority || 10; // [2] var topicIndex = 0, topics = topic.split( /\s/ ), topicLength = topics.length, added; for ( ; topicIndex < topicLength; topicIndex++ ) { topic = topics[ topicIndex ]; added = false; if ( !subscriptions[ topic ] ) { subscriptions[ topic ] = []; } // [3] var i = subscriptions[ topic ].length - 1, subscriptionInfo = { callback: callback, context: context, priority: priority }; // [4] for ( ; i >= 0; i-- ) { if ( subscriptions[ topic ][ i ].priority <= priority ) { subscriptions[ topic ].splice( i + 1, 0, subscriptionInfo ); added = true; break; } } // [5] if ( !added ) { subscriptions[ topic ].unshift( subscriptionInfo ); } } return callback; },
[1]. To understand this part, see the API diagram provided by amplify:
amplify.subscribe( string topic, function callback )amplify.subscribe( string topic, object context, function callback )amplify.subscribe( string topic, function callback, number priority )amplify.subscribe( string topic, object context, function callback, number priority )
As you can see, amplify allows multiple parameter formats. When the number and type of parameters are different, parameters at specific locations may be treated as different contents. This can also be seen in many other JavaScript libraries. In this way, the multi-parameter design can be achieved by judging the number and type of parameters.
[2] During subscription, the topic can contain spaces, and the blank space is considered as a separator. Therefore, a loop is used to associate a callback with multiple topics. Added is used as an identifier to indicate whether the newly added element has been added to the array. The initial value is false.
[3]. The storage of each callback is actually an object. Besides callback, context (default: null) and priority are included.
[4]. This loop is used to locate the corresponding position of the correlated element based on the value of priority. The correlated elements of any topic are from none to exist, and sorted by the priority value from small to large ). Therefore, in comparison, we assume that the priority value of the newly added element is large (with a low priority) and compare the values from the end of the array to the front, as long as the priority value of the correlated element in the original array is smaller than that of the newly added element, the loop can be interrupted and the newly added element can be added to the Array Using the splice method. If the cycle is always running, you can determine that the priority value of the newly added element is the smallest, and the value of added is the initial value false.
[5]. If the element is not added yet, run the Add command to determine that the element is located at the beginning of the array (or the first element ).
Unsubscribe
Although publishing and subscription are the most important, there will also be a time to unsubscribe (the magazine does not want to read it and return it decisively !). Therefore, an unsubscribe is required.
unsubscribe: function( topic, context, callback ) { if ( typeof topic !== "string" ) { throw new Error( "You must provide a valid topic to remove a subscription." ); } if ( arguments.length === 2 ) { callback = context; context = null; } if ( !subscriptions[ topic ] ) { return; } var length = subscriptions[ topic ].length, i = 0; for ( ; i < length; i++ ) { if ( subscriptions[ topic ][ i ].callback === callback ) { if ( !context || subscriptions[ topic ][ i ].context === context ) { subscriptions[ topic ].splice( i, 1 ); // Adjust counter and length for removed item i--; length--; } } }}
After reading the source code, this part seems easy to understand. Traverse the Associated Element Based on the specified topic, locate the consistent callback, and delete it. Because the splice method is used, the original array is directly modified, so you need to manually adjust the I and length.
Example of Amplify
One of the official examples is:
amplify.subscribe( "dataexample", function( data ) { alert( data.foo ); // bar});//...amplify.publish( "dataexample", { foo: "bar" } );
Combined with the source code, do you have a clearer understanding of the design pattern of publishing/subscription?
Additional instructions
You may also notice that the typical publishing/subscription implemented by AmplifyJS is synchronous ). That is to say, when running amplify. publish (topic), all the callbacks attached to a topic will be run without any delay.
Conclusion
Pub/Sub is a relatively easy-to-understand design model, but it is very useful to cope with the complex logic of large-scale applications. The AmplifyJS analyzed in this article is a JavaScript library that I think is well written and concise (for a single function), so I will share it with you here.