这道题主要是考察这三者在事件循环中的区别,事件循环中分为宏任务队列和微任务队列。

  • setTimeout的回调函数放到宏任务队列里,等到执行栈清空以后执行;
  • Promise.then里的回调函数会放到相应宏任务的微任务队列里,等宏任务里面的同步代码执行完再执行;
  • async函数表示函数里面可能会有异步方法,await后面跟一个表达式,async方法执行时,遇到await会立即执行表达式,然后把表达式后面的代码放到微任务队列里,让出执行栈让同步代码先执行。

练习题

题目1

async function fn1() {
    console.log(1);
    const result = await fn2();
    console.log(2);
}

async function fn2() {
    console.log(3);
    return new Promise((resolve, reject) => {
        resolve(); // 如果这里不执行resolve,那么将输出1 3 6 5
    }).then(() => {
        console.log(4)
    });
}

Promise.resolve().then(() => {
    console.log(5)
});

fn1();
console.log(6);
// 输出结果:1 3 6 5 4 2
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

需要注意以下几点:

  1. await只能在async函数中使用。
  2. await后面可以跟普通的函数,也可以跟带有then方法的对象,也就是thenable。
  3. 如果后面跟的是thenable时,await会收集thenable对象的原型对象上的then方法,并给其注入resolve和reject;然后阻塞当前作用域代码的执行,等待注入的resolve开启微任务异步队列的执行。如果后面不是thenable对象的话,直接开启微任务异步队列的执行。

题目2

async function async1() {
    console.log(1)
    const result = await async2();
    console.log(3)
}

async function async2() {
    console.log(2);
}

Promise.resolve().then(() => {
    console.log(4)
})

setTimeout(() => {
    console.log(5)
})

async1();
console.log(6);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

结果如下:1 2 6 4 3 5

分析:这道题涉及到的基本原理有以下几个方面:

  • 同步异步
  • 宏任务和微任务原理
  • promise原理
  • async-await原理

需要注意:async函数返回一个Promise对象,可以使用then方法添加回调函数。当函数执行的时候,一旦遇到await就会先返回,等到异步操作完成,再接着执行函数体内后面的语句。

参考文档

  1. setTimeout、Promise、Async/Await 的区别
  2. 第 8 题:setTimeout、Promise、Async/Await 的区别
  3. 浏览器的Tasks、microtasks、 queues 和 schedules
  4. 有道题,得细说(一道异步相关的面试题)