Take a look at the following example

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

console.log('script end');
Copy the code

The result is as follows:

script start
async1 start
async2 end
Promise
script end
promise1
async1 end
setTimeout
Copy the code

Note: The result printed in the new browser does not look like this

Why is it different? Let’s briefly mention that the V8 team took advantage of a Bug in Node8 and reduced the number of ticks from three to two at the bottom of the engine to improve performance. But that violates the norm, but the norm is human, and it can be changed under certain circumstances. The portal. You can see it here

We will still follow the specification for the print above

  • 1. First print script start.
  • 2. When async1() is executed, async1 start is printed first, because async expressions define functions that are also executed immediately
  • Saync2 () is an async defined function, so asynC2 end is executed and async2 returns a Promise. Important: the Promise returned will be placed in the callback queue. “Await” gives up the thread (js is single threaded) and then jumps out of async1 and executes further;
  • SetTimeout is a macro task that will not be executed until the execution stack (call stack) is cleared and all microtasks are completed. Therefore, setTimeout will be suspended and executed at last
  • 5. Then it comes to new Promise. New Promise is executed immediately, so it prints Promise immediately. (Promise is a function that executes immediately, but its success (resolve()) or failure (reject()) callback is really a callback that executes asynchronously.) Then, when resolve() is executed, the resolve() task is placed in the callback queue, and when the call stack is free, the Event Loop picks it up, breaking the Promise and continuing
  • 6. Output script end;
  • 7. The synchronization task has been completely executed and the execution stack is now free, so the event loop will fetch the task from the callback queue and put it into the execution stack for execution;
  • 8. At this time, the first task is the Promise async2 put in. When the Promise is executed, the resolve function is encountered, and resolve is put into the task queue to wait for the next task
  • 9. Next up is the resolve callback put in by the new Promise, which is executed by the stack and outputs promise1, and proceeds to the next task
  • Async2 has no return content, so the resolve argument is undefined. At this point, the Promise defined by await has been completed and the result is returned. So we can proceed to the task after async1 and print async1 end
  • 11. After the execution of the microtask above, start the asynchronous setTimeout in the macro task

Summary: The order of Event Loop execution is as follows:

  • 1 Perform the synchronization task first, which is a macro task
  • 2. If the execution stack is empty, check whether asynchronous code needs to be executed
  • 3. Perform all microtasks
  • 4. Render the page if necessary after performing all the microtasks
  • 5. Then start the next Event Loop, which executes the asynchronous code in the macro task, namely the callback function in setTimeout

Macro task:

  • 1.script
  • 2.setTimeout
  • 3.setInterval
  • 4.setImmediate
  • 5.I/O
  • 6.UI rendering

Micro tasks:

  • 1. Process. NextTick (unique) node
  • 2.promise
  • 3.MutationObserver