A deep understanding of the synchronous and asynchronous mechanisms in JavaScript programming and a deep understanding of javascript
One of the advantages of JavaScript is how it handles asynchronous code. Asynchronous code will be put into an event queue and will not block the thread until all other code is executed. However, it may be difficult for beginners to write asynchronous code. In this article, I will eliminate any confusion you may have.
Understand asynchronous code
The most basic asynchronous functions of JavaScript are setTimeout and setInterval. SetTimeout will execute the given function after a certain period of time. It accepts a callback function as the first parameter and a millisecond time as the second parameter. The following is an example:
console.log( "a" );setTimeout(function() { console.log( "c" )}, 500 );setTimeout(function() { console.log( "d" )}, 500 );setTimeout(function() { console.log( "e" )}, 500 );console.log( "b" );
As expected, the console first outputs "a" and "B". After about 500 milliseconds, we can see "c", "d", and "e ". I use "approx" because setTimeout is actually unpredictable. In fact, even the HTML5 specification mentioned this problem:
- "This API cannot ensure that timing runs as scheduled and accurately. Latency caused by CPU load and other tasks is predictable ."
Interestingly, timeout does not occur until all the remaining code in the same program segment is executed. Therefore, if you set a timeout and execute a function that requires a long time to run, the function will not even start until the function is executed. In fact, asynchronous functions, such as setTimeout and setInterval, are pushed into a queue called Event Loop.
Event Loop is a callback function queue. When an asynchronous function is executed, the callback function is pushed into this queue. The JavaScript engine starts to process the event loop until the asynchronous function is executed. This means that JavaScript code is not multi-threaded, even if the behavior is similar. An event loop is a FIFO queue, which indicates that the callback is executed in the order they are added to the queue. JavaScript is chosen as a development language by node because it is so easy to write such code.
Ajax
Asynchronous Javascript and XML (AJAX) change the Javascript language permanently. Suddenly, the browser no longer needs to reload to update the web page. Ajax code implementation in different browsers may be long and boring; but thanks to jQuery (and other libraries, we can implement client-server communication in an easy and elegant way.
We can use jQuery's cross-browser interface $. ajax to easily retrieve data, but it cannot render what happened behind the scenes. For example:
var data;$.ajax({ url: "some/url/1", success: function( data ) { // But, this will! console.log( data ); }})// Oops, this won't work...console.log( data );
The easy mistake is to use data immediately after calling $. ajax, but it is actually like this:
xmlhttp.open( "GET", "some/ur/1", true );xmlhttp.onreadystatechange = function( data ) { if ( xmlhttp.readyState === 4 ) { console.log( data ); }};xmlhttp.send( null );
The underlying XmlHttpRequest object initiates a request and sets the callback function to process the XHR readystatechnage event. Then execute the XHR send method. During XHR running, the readystatechange event is triggered when its attribute readyState changes. The callback function is executed only when the XHR receives a response from the remote server.
Process asynchronous code
Asynchronous programming is easy to fall into what we often call "Callback hell ". In fact, almost all asynchronous functions in JS Use callback. The result of continuous execution of several asynchronous functions is nested callback functions and the complicated code that comes with them.
Many functions in node. js are asynchronous. Therefore, the following code is common:
var fs = require( "fs" );fs.exists( "index.js", function() { fs.readFile( "index.js", "utf8", function( err, contents ) { contents = someFunction( contents ); // do something with contents fs.writeFile( "index.js", "utf8", function() { console.log( "whew! Done finally..." ); }); });});console.log( "executing..." );
The following client code is also widely used:
GMaps.geocode({ address: fromAddress, callback: function( results, status ) { if ( status == "OK" ) { fromLatLng = results[0].geometry.location; GMaps.geocode({ address: toAddress, callback: function( results, status ) { if ( status == "OK" ) { toLatLng = results[0].geometry.location; map.getRoutes({ origin: [ fromLatLng.lat(), fromLatLng.lng() ], destination: [ toLatLng.lat(), toLatLng.lng() ], travelMode: "driving", unitSystem: "imperial", callback: function( e ){ console.log( "ANNNND FINALLY here's the directions..." ); // do something with e } }); } } }); } }});
Nested callbacks can get really nasty, but there are several solutions to this style of coding.
Nested callback can easily bring about "Bad taste" in the code, but you can try to solve this problem in the following ways:
- The problem isn't with the language itself; it's with the way programmers use the language-Async Javascript.
There is no bad language, and only bad programmers -- asynchronous ript
Naming Functions
A convenient solution to clear nested Callbacks is to avoid nested layers. Pass a name function as the callback parameter, instead of passing an anonymous function:
var fromLatLng, toLatLng;var routeDone = function( e ){ console.log( "ANNNND FINALLY here's the directions..." ); // do something with e};var toAddressDone = function( results, status ) { if ( status == "OK" ) { toLatLng = results[0].geometry.location; map.getRoutes({ origin: [ fromLatLng.lat(), fromLatLng.lng() ], destination: [ toLatLng.lat(), toLatLng.lng() ], travelMode: "driving", unitSystem: "imperial", callback: routeDone }); }};var fromAddressDone = function( results, status ) { if ( status == "OK" ) { fromLatLng = results[0].geometry.location; GMaps.geocode({ address: toAddress, callback: toAddressDone }); }};GMaps.geocode({ address: fromAddress, callback: fromAddressDone});
In addition, the async. js library can help us deal with multiple Ajax requests/responses. For example:
async.parallel([ function( done ) { GMaps.geocode({ address: toAddress, callback: function( result ) { done( null, result ); } }); }, function( done ) { GMaps.geocode({ address: fromAddress, callback: function( result ) { done( null, result ); } }); }], function( errors, results ) { getRoute( results[0], results[1] );});
This code executes two asynchronous functions. Each function receives a callback function named "done" and calls it at the end of the function. After the two "done" Callback functions are complete, the callback functions of the parallel function are called and executed or processed to generate the results or errors of the two asynchronous functions.
Promises Model
Derived from CommonJS/:
- Promise indicates the final result returned after an operation is completed independently.
Many libraries contain the promise model. jQuery already has a usable and outstanding promise API. JQuery introduces the Deferred object in version 1.5, and can use jQuery. Deferred to construct the result in the function that returns promise. The function that returns promise is used to execute an asynchronous operation and solve the latency after completion.
var geocode = function( address ) { var dfd = new $.Deferred(); GMaps.geocode({ address: address, callback: function( response, status ) { return dfd.resolve( response ); } }); return dfd.promise();};var getRoute = function( fromLatLng, toLatLng ) { var dfd = new $.Deferred(); map.getRoutes({ origin: [ fromLatLng.lat(), fromLatLng.lng() ], destination: [ toLatLng.lat(), toLatLng.lng() ], travelMode: "driving", unitSystem: "imperial", callback: function( e ) { return dfd.resolve( e ); } }); return dfd.promise();};var doSomethingCoolWithDirections = function( route ) { // do something with route};$.when( geocode( fromAddress ), geocode( toAddress ) ). then(function( fromLatLng, toLatLng ) { getRoute( fromLatLng, toLatLng ).then( doSomethingCoolWithDirections ); });
This allows you to execute two asynchronous functions, wait for their results, and then use the results of the previous two calls to execute another function.
- Promise indicates the final result returned after an operation is completed independently.
In this Code, the geocode method executes twice and returns a promise. The asynchronous function is executed and the resolve is called in the callback. Then, once the two calls to resolve are completed, then will execute and receive the returned results of the previous two calls to geocode. After the result is passed in getRoute, This method also returns a promise. Finally, after the promise of getRoute is solved, the doSomethingCoolWithDirections ctions callback is executed.
Event
An event is another communication method after asynchronous callback completes processing. An object can become a transmitter and dispatch events, while other objects listen to these events. This type of event processing method is called the observer mode. The backbone. js library creates such a function module in withBackbone. Events.
var SomeModel = Backbone.Model.extend({ url: "/someurl"});var SomeView = Backbone.View.extend({ initialize: function() { this.model.on( "reset", this.render, this ); this.model.fetch(); }, render: function( data ) { // do something with data }});var view = new SomeView({ model: new SomeModel()});
There are other mixed examples and function libraries used to launch events, such as jQuery Event Emitter, EventEmitter, monologue. js, And the built-in EventEmitter module of node. js.
- An event loop is a queue of callback functions.
A similar message sending method is called the intermediary mode, which is used in the postal. js library. In the intermediary mode, there is a man-in-the-middle for listening to and dispatching events to all objects. In this mode, an object is not directly associated with another object, so that the objects are separated from each other.
Never return promise to a public API. This not only relates to the use of promises by API users, but also makes refactoring more difficult. However, the combination of promises for internal purposes and events for external interfaces can lower the coupling of applications and facilitate testing.
In the previous example, the doSomethingCoolWithDirections callback function is executed after the two geocode functions are completed. Then, doSomethingCoolWithDirections will get the response received from getRoute and then send it as a message.
var doSomethingCoolWithDirections = function( route ) { postal.channel( "ui" ).publish( "directions.done", { route: route });};
This allows other parts of the application to respond to asynchronous callback without directly referencing the requested object. When obtaining commands, it is very likely that many areas of the page need to be updated. In a typical jQuery Ajax process, when the received command changes, a smooth callback may have to be adjusted accordingly. This may make the code difficult to maintain, but it is much easier to process updates in multiple areas of the UI by using messages.
var UI = function() { this.channel = postal.channel( "ui" ); this.channel.subscribe( "directions.done", this.updateDirections ).withContext( this );};UI.prototype.updateDirections = function( data ) { // The route is available on data.route, now just update the UI};app.ui = new UI();
Other libraries that transmit messages in the intermediary mode include amplify, PubSubJS, and radio. js.
Conclusion
JavaScript makes it easy to write asynchronous code. use promises, events, or naming functions to avoid "callback hell ". for More javascript asynchronous programming information, click Async JavaScript: Build More Responsive Apps with Less. more instances are hosted on github. Address: NetTutsAsyncJS. Please Clone it!