Objective
The most basic asynchronous invocation in JS is callback, which passes the callback function callback to the asynchronous API, which notifies the JS engine to invoke callback after the asynchronous completion of the browser or Node. For a simple asynchronous operation, it is sufficient to implement it with callback. But as the interactive page and Node appeared, the drawbacks of the callback scheme began to emerge. The Promise specification was conceived and incorporated into the ES6 norm. Later ES7 the async function into the standard on the basis of Promise. This is a history of JavaScript asynchronous evolution.
Synchronous vs. asynchronous
Typically, the code is executed from the top down. If you have more than one task, you must queue, the previous task is completed, and the next task will not execute. This mode of execution is called Synchronization (synchronous). It is easy for beginners to confuse the synchronization in computer terminology with the synchronization in everyday language. For example, "Sync files to the cloud" in sync, meaning "make ... Consistency. " In the computer, synchronization refers to the pattern that the task executes from the top down. Like what:
A (); B (); C ();
In this code, a, B, and C are three different functions, each of which is an unrelated task. In synchronous mode, the computer performs A task, performs A B task, and finally executes the C task. In most cases, the sync mode is fine. But if the B task is a lengthy network request, and the C task happens to be a new page, it will cause the page to stutter.
A better solution would be to divide the B task into two parts. Part of the task that immediately executes the network request, and the other part executes the task after the request comes back. This part is executed immediately, and another part of the future execution pattern is called async.
A (); // Ajax (' url1 ',function B () { // execute at some time in the future }) C (); // execution order A + C = B
In fact, the JS engine does not directly handle the task of the network request, it just invokes the browser's network request interface, the browser sends the network request and listens for the returned data. The nature of JavaScript async capability is the ability of the browser or Node to multithreading.
Callback
The functions that are executed in the future are often called callback. Using the asynchronous mode of callback solves the problem of blocking, but it also brings some other problems. In the beginning, our function is written from the top down, but also from the top down, this "linear" pattern, very consistent with our thinking habits, but now is callback interrupted! In the above section of code, now it skips the B task to perform the C task first! This asynchronous "non-linear" code is more difficult to read than synchronizing "linear" code, so it's easier to breed bugs.
Try to determine the order of execution of the code below, and you will be more difficult to read than the "linear" code for "nonlinear" code.
A (); Ajax (function() { B (); Ajax (function() { C (); } D (); }); E (); // A = E = B and D and C
In this code, the order of execution from the top down is Callback. Our reading code line of Sight is a, B, C, D, and E, but the order of execution is a and E and B and D and C, which is the bad thing about the nonlinear code.
By advancing the tasks performed behind Ajax, it is easier to understand the order in which the code is executed. Although the code looks ugly because of nesting, the order of execution now is a "linear" approach from top to bottom. This technique is useful when writing multiple nested code.
A (); E (); Ajax (function() { B (); D (); Ajax (function() { C (); } }); // A = E = B and D and C
The last piece of code only handles a successful callback and does not handle the exception callback. Next, add the exception handling callback and discuss the problem of "linear" code execution.
A (); Ajax (function() { B (); Ajax (function() { C (); },function() { D (); }); } ,function() { E (); });
After the exception handling callback, URL1 's successful callback function B and the exception callback function, E, are separated. This "non-linear" situation has arisen again.
In node, the error-first strategy was developed in order to solve the "nonlinear" problem caused by the abnormal callback. The first parameter of callback in node, specifically to determine if an exception occurred
A (); Get (function(error) { if(error) { E (); } Else { B (); Get (function(error) { if(error) { D (); } Else { C ();}}) ;} );
To this, the "nonlinear" problem caused by callback is basically solved. Unfortunately, using callback nesting, layer if else, and callback functions is not very convenient to read once the number of nested layers is multiple. In addition, callback can only handle exceptions within the current callback function once an exception occurs.
Promise
In the history of the asynchronous evolution of JavaScript, there emerges a series of libraries to solve callback's drawbacks, and Promise becomes the ultimate winner and is successfully introduced into ES6. It provides a better "linear" notation and resolves an issue in which an asynchronous exception can only be captured in the current callback.
Promise is like an intermediary that promises to return a trustworthy asynchronous result. First, Promise and asynchronous interface signed a protocol, when successful, call resolve function notification Promise, exception, call reject Notification Promise. On the other hand, Promise and callback also signed an agreement by Promise to return the trusted values in the future to the callback registered in then and catch.
// Create a Promise instance (asynchronous interface and Promise sign protocol) var promise = new Promise (function (Resolve,reject) {ajax ( ' url ' , resolve,reject);}); // Promise.then (function // success }). catch (function // error })
Promise is a very good intermediary, it only returns the trusted information to callback. It makes some processing of the results of the third-party asynchronous library, which guarantees that the callback will be called asynchronously and will only be called once.
varPromise1 =NewPromise (function(resolve) {//may cause synchronous calls for some reasonResolve (' B ');});//promise will still execute asynchronouslyPromise1.then (function(value) {Console.log (value)}); Console.log (A);//a B (first a after B)varPromise2 =NewPromise (function(resolve) {//a successful callback was notified 2 timesSetTimeout (function() {resolve (); },0)});//promise will only be called oncePromise2.then (function() {Console.log (A)});//A (only one)varPROMISE3 =NewPromise (function(resolve,reject) {//The successful callback is notified first, and the failure callback is notified.SetTimeout (function() {resolve (); Reject (); },0)});//promise will only invoke a successful callbackPromise3.then (function() {Console.log (A)}).Catch(function() {Console.log (B)});//A (only a)
View Code
After describing the features of Promise, we look at how it uses chained calls to solve the problem of the readability of asynchronous code.
varFetch =function(URL) {//returns a new instance of Promise return NewPromise (function(Resolve,reject) {ajax (Url,resolve,reject); });} A (); Fetch (' Url1 '). Then (function() {B (); //returns a new instance of Promise returnFetch (' URL2 ');}).Catch(function(){ //A new Promise instance can also be returned when the exception is returnFetch (' URL2 '); //use chained notation to invoke the then method of this new Promise instance}). Then (function() {C (); //continue to return a new Promise instance ...})//A B C ...
View Code
Again and again, a Promise object is returned, which is invoked continuously in a chained way. The problem of callback layer nesting and the "nonlinear" execution of asynchronous code are Promise.
Another difficulty Promise solves is that callback can only catch the current error exception. Promise and callback different, each callback can only know their own error situation, but Promise agent all callback, all callback error, can be handled by Promise Unified. Therefore, catch can be used to catch an exception that was not previously caught.
Promise solves the callback asynchronous invocation problem, but Promise does not get rid of callback, it simply puts callback into a trusted intermediary, an intermediary that links our code to the asynchronous interface.
Asynchronous (Async) functions
Asynchronous (Async) function is a new feature of ES7, it combines Promise, let us get rid of the callback of the binding, directly with the class synchronous "linear" way, write asynchronous function.
To declare an asynchronous function, simply add a keyword async before the normal function, such as async function main () {}. In an async function, you can use the await keyword, which represents the result of waiting for the execution of the subsequent expression, typically followed by an Promise instance.
function main{ // timer is the var value defined in the previous example = await timer (+); // done (returned after 100ms) }main ();
The Async function calls main () just like a normal function. After the call, the first line of code in the async function is executed immediately var value = await timer (100). The next line of code is not executed until the asynchronous execution completes.
In addition, asynchronous functions are basically similar to other functions, which use Try...catch to catch exceptions. You can also pass in parameters. However, do not use return in an async function to return a value.
varTimer =NewPromise (functionCreate (resolve,reject) {if(typeofDelay!== ' number ') {Reject (NewError (' type error ')); } setTimeout (Resolve,delay,' Done ');}); AsyncfunctionMain (delay) {Try{ varValue1 =await timer (delay); varvalue2 = await timer (' '); varValue3 =await timer (delay); }Catch(Err) {console.error (err); //Error:type Error //At Create (<anonymous>:5:14) //At timer (<anonymous>:3:10) //At A (<anonymous>:12:10)}}main (0);
View Code
Asynchronous functions can also be treated as values, passed into normal functions and executed in asynchronous functions. However, in asynchronous functions, it is important to note that asynchronous functions are executed synchronously if you do not use await.
function Main (delay) { var value1 = await timer (delay); Console.log (' A 'function doasync (main) { main (0); Console.log (' B ')}doasync (main); // B A
The value printed at this time is B a. Indicates that the Doasync function did not wait for the asynchronous execution of main to execute the console. If you want the console to execute after the asynchronous execution of main, we need to add the keyword await before main.
function Main (delay) { var value1 = await timer (delay); Console.log (' A 'function doasync (main) { await main (0); Console.log (' B ')}doasync (main); // A B
Because asynchronous functions are written in a class-synchronous way, a novice may write as follows when dealing with multiple concurrent requests. This causes the URL2 request to be sent only when the URL1 request comes back.
var function (URL) { returnnew Promise (function (resolve,reject) { ajax (URL, Resolve,reject); function Main () { try{ var value1 = await fetch (' URL1 '); var value2 = await fetch (' url2 '); Conosle.log (value1,value2); } Catch (Err) { console.error (err) }}main ();
View Code
Use the Promise.all method to solve this problem. Promise.all is used to wrap multiple Promise instances into a new Promis e instance, when all Promise The resolve function of Promise.all is triggered after success, and the Reject function of Promise.all is called immediately when there is a failure.
var function (URL) { returnnew Promise (function (resolve,reject) { ajax (URL, Resolve,reject); function Main () { try{ var arrvalue = await promise.all[fetch (' url1 '), Fetch (' URL2 ')]; Conosle.log (arrvalue[0],arrvalue[1]); } Catch (Err) { console.error (err) }}main ();
View Code
Currently using Babel has supported transcoding of ES7 async functions, and you can start experimenting on your own projects.
"Turn" JavaScript asynchronous evolutionary history