Words do not say, first on the topic, is said to be two years ago a rotten street headline interview questions

async function async1() {
    console.log('async1 start');
    await async2();                
    console.log('async1 end');
}
async function async2() {
    console.log('async2');
}
console.log('script start');
setTimeout(function() {
    console.log('setTimeout');
}, 0)
async1();
new Promise(function(resolve) {
    console.log('promise1');
    resolve();
}).then(function() {
    console.log('promise2');
});
console.log('script end');
Copy the code

So what’s the print order? Which involves the eventloop js/async/await/promise, can try to answer it Released the following answer

script start
async1 start
async2
promise1
script end
async1 end
promise2
undefined
setTimeout
Copy the code

The above execution result is the result of running on Chrome 96.0, and may be inconsistent with the results of previous years, mainly due to the Chrome version

Now, why is this order

“Await” blocks the following task, meaning the next line of code, and “await” is executed immediately

First of all, we need to be clear about the concept that in fact async await is essentially just a syntactic sugar for promise. Then, functions with async keyword make your function return a promise object. If async keyword function returns a promise object, Resolve () is automatically wrapped in promise.resolve (), and if the async keyword function explicitly returns a Promise, the Promise you return will prevail.

With await, if await is not a promise object, then await blocks the following code, executes the synchronous code outside the async function, completes the synchronous code, and then goes back inside the async, taking the non-promise thing as the result of await expression. If await is followed by a promise object, the promise object will be fulfilled after the async external synchronization code is fulfilled, and then the resolve parameter will be used as the result of the await expression. Next, put a macro task micro task diagram for easy understanding

In the event loop of each layer (once), the whole code block is first treated as a macro task, in which Promise (then, catch, finally), MutationObserver, process. nextTick are the micro tasks of the macro task layer. Macro task synchronization code into the main thread of the immediate execution of a, micro macro task in the task of asynchronous code will serve as the macro tasks of the next cycle into the call stack, to be performed at this point, the call stack for waiting in the queue is divided into two kinds, respectively is the high priority in this layer circulation task queue, as well as low priority of the next cycle performance of macro task queue.

Each macro task queue can be understood as the current main thread. Js always executes the task on the main thread first, and then executes all the micro tasks on the current macro task queue. The principle of first-in, first-out is that the next macro task will be executed only after all the micro tasks on this macro task queue are completed.

  1. Whether synchronous or asynchronous, JS executes sequentially, just without waiting for the result of asynchronous execution
  2. Synchronous tasks have no priorities. Asynchronous tasks have priorities. Microtasks are executed first and then macrotasks are executed in the same order

Microtasks: Process. nextTick, Promise, MutationObserver

Macro tasks: Script, setTimeout, setInterval, setImmediate, I/O, UI Rendering

First, JS is single-threaded, so tasks on the main thread are executed first

  1. That isconsole.log('script start')The output is executed firstscript start
  2. And then I ran intosetTimeout()And put it into the next macro task queue until the execution of the current macro task queue and its micro task queue is complete
  3. Then performasync1()Function, which essentially creates a Promise object, and the promise constructor runs on the main task queue, so it executes immediatelyasync1 startAnd then executeasync2()Function, and returns oneasync2.then(()=>{console.log('async1 end'); })Then () will be put into the microtask queue, which we’ll call Task1, and will wait for the main thread to finish executing, which will also executeasync2()Constructor of, outputasync2.
  4. Then we execute new Promise(), which prints the contents of the constructor directly, so promise1 is printed.
  5. Then resolve() will enter then(),promise.then(console.log('promise2');)Is an asynchronous task that will be put into the microtask queue, and we’ll name it Task2.
  6. Then the final main thread task is performedconsole.log('script end');The outputscript end.
  7. When synchronization tasks on the main thread are complete, task1 and task2 are executed, and async1 end and promise2 are output in sequence. The microtask queue is complete
  8. The next macro task queue is then executed, i.esetTimeout()And the outputsetTimeout

And then, if there are more than one await, what is the order of execution between it and promise?

async function async1() {
    console.log('async1 start');
    await async2();                
    console.log('async1 end');
}
async function async2() {
    await async3(); 
    console.log('async2');
}
async function async3() {
    console.log('async3');
}
console.log('script start');
setTimeout(function() {
    console.log('setTimeout');
}, 0)
async1();
new Promise(function(resolve) {
    console.log('promise1');
    resolve();
}).then(function() {
    console.log('promise2');
});
console.log('script end');
Copy the code

The result in Chrome is as follows:

script start
async1 start
async3
promise1
script end
async2
promise2
async1 end
undefined
setTimeout
Copy the code

Here for

async2
promise2
async1 end
Copy the code

There may be some confusion about the order here, I will explain it according to my own understanding, and I hope you can correct it if there is something wrong:

The first part is pretty much the same, but async1() is executed in step 3 above; Console. log(‘async1 start’); Async1 start and await async2(); The contents of async2() function are first executed, and async2().then(()=>{console.log(‘async1 end’); }), and await async2() with await async3(); Async3 (), console.log(‘async3’); Then (()=>{console.log(‘async2’)}). So it can abstract into async3 (). Then (() = > {the console. The log (‘ async2 ‘)}), then (() = > {the console. The log (‘ async1 end ‘); }), here named task1, and then the same process in between, promise.then(console.log(‘promise2’);) Async3 ().then(()=>{console.log(‘async2’)}) {console.log(‘async2’)}) {console.log(‘async2’)}) {console.log(‘async2’)}); Then (()=>{console.log(‘async1 end’); }), execute task2 (promise2), and then execute task3 (async1 end).