preface

I met such a question in my interview. The execution order of async/await, Promise and setTimeout was not done correctly.

I looked it up later and it was a very classic title. Also give you answer questions, say their own understanding.


So the question is look at the code.

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

You can do it yourself. Let’s see if the sum is correct, and the result will be published below.


So let’s analyze this problem, and we need to know a couple of concepts.

EventLoop an EventLoop mechanism

There are two types of JavaScript events, macro-tasks and micro-tasks.

  • Macro tasks: including the overall code script, setTimeout, setInterval
  • Microtasks: Promise.then(not new Promise), process.nexttick (in node)
  • The execution sequence of events is macro task first, then micro task, which is the basis. Tasks can be synchronous task and asynchronous task. Synchronous task enters the main thread, asynchronous task enters the Event Table and registers the function. The callback function is placed in the Event Queue (macro tasks and micro tasks are different Event queues). After the synchronization task is completed, the Event Queue is read from the Event Queue and put into the main thread for execution. The callback function may also contain different tasks, so the above operation is repeated.
  • See EventLoop for details

Note: setTimeOut does not directly put your callback into the above asynchronous queue. Instead, setTimeOut puts the callback into the execution asynchronous queue after the timer expires. If there are already many tasks in the queue, get behind them. This explains why setTimeOut cannot be executed accurately. The setTimeOut execution needs to satisfy two conditions:

  1. The main process must be idle. If the time is up, the main process will not be idle and will not execute your callback function
  2. The callback function will not be executed until all previous asynchronous functions have finished executing when the asynchronous queue is inserted

The above is the official explanation, say your own understanding:

Now that you know what macro tasks and microtasks are, it’s a lot easier to understand. First, do macro tasks => Event Queue of microtasks => Event Queue of macro tasks

Promise, async/await

  1. First, new promises are synchronous tasks that are put into the main process to be executed immediately (think of as an immediate execution function). whileThe then() function is that asynchronous tasks are placed in an asynchronous queueWhen do I put it on an asynchronous queue? When your promise state ends (reject or resolve), it is immediately put into an asynchronous queue.
  2. A function with the async keyword returns a promise object and, if there is no await in it, executes as if it were a normal function; Async is not very cool without await
  3. Await keyword should be inside async keyword function, await keyword outside will report an error; To await, as it means, is to wait for the expression on the right to complete. At this timeAwait gives away the thread.Block subsequent code inside async to execute code outside async. Wait for the outside synchronization code to complete, will execute the subsequent code inside.Even if the await object is not a promise object, it is a synchronization function, will also wait for this operation

The mechanism of await

“Await” is generally considered to be waiting for an async function to complete. To be exact, await is an expression that contains a Promise object or some other value.

Since async returns a Promise object, await can be used to wait for the return value of an async function — it can also be said to await async function, but to be clear, it is actually waiting for a return value. Await can actually be followed by ordinary function calls or direct quantities.

Await waits for what it is waiting for, a Promise object, or some other value, and then what? Await is an operator used to form an expression, and the result of an await expression depends on what it is waiting for.

If it waits for something other than a Promise object, the result of the await expression operation is what it waits for.

If it waits for a Promise object, await is busy and blocks the following code, waiting for the Promise object resolve, and then getting the value of resolve as the result of the await expression. This is why await must be used in async functions. Async function calls do not block, and all blocking within them is wrapped up in a Promise object and executed asynchronously.

Ruan yifeng’s explanation is easier for me to understand:

An async function returns a Promise object. When the function executes, it returns an await object, waits until the triggered asynchronous operation is complete, and then executes the following statement in the function body. It can be understood as giving up the thread, out of the async function body

The answer is published ✨

script start
async1 start
async2
promise1
script end
asnyc1 end
promise2
setTimeOut
Copy the code

Analysis to the

  1. Execute console.log(‘script start’) and print script start;

  2. Execute setTimeout, which is an asynchronous action, and place it in the macro task asynchronous queue;

  3. Execute async1(), print async1 start, and continue;

  4. Async2 () is executed, async2 is output, and a promise object is returned. Await gives out the thread, add the returned promise to the microtask async queue, await gives out the thread. The following code is blocked, jumping out of async1. So the code below async1() also waits for the above to complete;

  5. Execute new Promise, print promise1, and place resolve on the microtask asynchronous queue;

  6. Execute console.log(‘script end’) to print script end;

  7. The code that has been synchronized up to this point is finished executing and then goes to the microtask asynchronous queue to fetch the task

  8. Next, resolve (returned by async2 promise) outputs async1 end.

  9. Then execute resolve (new Promise), which outputs promise2.

  10. Finally, setTimeout is executed, and setTimeout is printed.


Personal understanding, welcome to point out. 🎉