跳到主要内容

Promise 练习题

async 函数的执行顺序示例

let n = 0;
// async 如果里面没有await 就是一个普通函数
(async function a(num) {
console.log('1-', num); // 同步1 —— 1 0
// 先传参后加减
b(num++);
console.log('2-', num); // 同步5 —— 2 1
})(n);

async function b(num) {
console.log('3-', num); // 同步2 —— 3 0
// 先传参后加减
await c(num++); // 相当于 Promise.resolve().then(res => console.log('4-',++num);)
console.log('4-', ++num); // 微任务1 —— 4 1
}

async function c(num) {
console.log('5-', num); // 同步3 —— 5 0
// 异步任务 注册
setTimeout(() => {
console.log('6-', num); // 异步任务 —— 6 1
});
console.log('7-', ++num); // 同步4 —— 7 1
}

new Promise((resolve) => {
console.log('8-', n); // 同步6 —— 8 0
resolve(n);
}).then((res) => {
console.log('9-', n++); // 微任务2 —— 9 1
});

console.log('10-', n++ > n); // 同步7 —— 10 false
console.log('11-', n); // 同步8 —— 11 1

在这段代码中,涉及多个 async 函数、Promise 以及 setTimeout 的混合使用。以下是代码的执行顺序及其原因:

  1. 初始化变量

    • let n = 0; 初始化变量 n0
  2. 执行 IIFE (立即调用的异步函数)

    • (async function a(num) { ... })(n); 立即调用异步函数 a,传入当前的 n0
  3. 函数 a 执行

    • console.log('1-', num); 输出 1- 0
    • 调用 b(num++),传入当前 num0,然后 num 自增为 1
    • console.log('2-', num); 输出 2- 1
  4. 函数 b 执行

    • console.log('3-', num); 输出 3- 0
    • 调用 await c(num++),传入当前 num0,然后 num 自增为 1
  5. 函数 c 执行

    • console.log('5-', num); 输出 5- 0
    • 设置一个 setTimeout,将在宏任务队列中注册一个回调,延迟执行 console.log('6-', num);
    • console.log('7-', ++num); 先将 num 自增为 1,然后输出 7- 1
  6. 继续执行函数 b

    • await c(num++) 等待 c 函数执行完毕。由于 c 中没有返回 Promise,默认返回 undefined,因此继续执行后续代码。
    • console.log('4-', ++num);num 自增为 2,然后输出 4- 2。此时,b 函数的微任务完成。
  7. 执行 Promise

    • new Promise((resolve) => { ... }).then(res => { ... }); 创建一个新的 Promise
    • console.log('8-', n); 输出 8- 0
    • resolve(n); 立即解析 Promise,将 n 的值 0 传递给下一个 then
    • 在微任务队列中注册 then 回调,输出 9- 1(因为 n++n 变为 1)。
  8. 同步代码继续执行

    • console.log('10-', n++ > n); 计算 0 > 1,结果为 false,然后 n 自增为 1。输出 10- false
    • console.log('11-', n); 输出 11- 1
  9. 处理微任务队列

    • 先执行 b 函数中 console.log('4-', ++num); 输出 4- 2
    • 然后执行 Promisethen 回调,输出 9- 1
  10. 处理宏任务队列

    • 执行 setTimeout 回调,输出 6- 1

最终输出顺序:

1- 0
3- 0
5- 0
7- 1
8- 0
10- false
11- 1
4- 2
9- 1
6- 1

详细解释:

  • 同步任务 按照代码顺序依次执行,输出 1- 03- 05- 07- 18- 010- false11- 1
  • 微任务 包含 async 函数中的 await 后的代码和 Promise.then 回调,先执行 4- 2,然后执行 9- 1
  • 异步任务 包含 setTimeout 回调,最后执行 6- 1

关键点解析:

  • async 函数的行为:

    • 如果 async 函数内部没有 await,则其行为类似于普通函数,所有同步代码立即执行。
    • 一旦遇到 await,函数会暂停执行,等待 await 的 Promise 完成后再继续执行后续代码,这部分代码会作为微任务加入微任务队列中。
  • PromisesetTimeout 的区别:

    • Promise 的回调(如 then)属于微任务,优先于宏任务执行。
    • setTimeout 的回调属于宏任务,会在所有微任务完成后执行。
  • 变量自增操作的顺序:

    • num++ 是后缀自增,先传递当前值,再自增。
    • ++num 是前缀自增,先自增,再传递新值。