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
的混合使用。以下是代码的执行顺序及其原因:
-
初始化变量
let n = 0;
初始化变量n
为0
。
-
执行 IIFE (立即调用的异步函数)
(async function a(num) { ... })(n);
立即调用异步函数a
,传入当前的n
值0
。
-
函数
a
执行console.log('1-', num);
输出1- 0
。- 调用
b(num++)
,传入当前num
值0
,然后num
自增为1
。 console.log('2-', num);
输出2- 1
。
-
函数
b
执行console.log('3-', num);
输出3- 0
。- 调用
await c(num++)
,传入当前num
值0
,然后num
自增为1
。
-
函数
c
执行console.log('5-', num);
输出5- 0
。- 设置一个
setTimeout
,将在宏任务队列中注册一个回调,延迟执行console.log('6-', num);
。 console.log('7-', ++num);
先将num
自增为1
,然后输出7- 1
。
-
继续执行函数
b
await c(num++)
等待c
函数执行完毕。由于c
中没有返回Promise
,默认返回undefined
,因此继续执行后续代码。console.log('4-', ++num);
将num
自增为2
,然后输出4- 2
。此时,b
函数的微任务完成。
-
执行
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
)。
-
同步代码继续执行
console.log('10-', n++ > n);
计算0 > 1
,结果为false
,然后n
自增为1
。输出10- false
。console.log('11-', n);
输出11- 1
。
-
处理微任务队列
- 先执行
b
函数中console.log('4-', ++num);
输出4- 2
。 - 然后执行
Promise
的then
回调,输出9- 1
。
- 先执行
-
处理宏任务队列
- 执行
setTimeout
回调,输出6- 1
。
- 执行
最终输出顺序:
1- 0
3- 0
5- 0
7- 1
8- 0
10- false
11- 1
4- 2
9- 1
6- 1
详细解释:
- 同步任务 按照代码顺序依次执行,输出
1- 0
、3- 0
、5- 0
、7- 1
、8- 0
、10- false
、11- 1
。 - 微任务 包含
async
函数中的await
后的代码和Promise.then
回调,先执行4- 2
,然后执行9- 1
。 - 异步任务 包含
setTimeout
回调,最后执行6- 1
。
关键点解析:
-
async
函数的行为:- 如果
async
函数内部没有await
,则其行为类似于普通函数,所有同步代码立即执行。 - 一旦遇到
await
,函数会暂停执行,等待await
的 Promise 完成后再继续执行后续代码,这部分代码会作为微任务加入微任务队列中。
- 如果
-
Promise
和setTimeout
的区别:Promise
的回调(如then
)属于微任务,优先于宏任务执行。setTimeout
的回调属于宏任务,会在所有微任务完成后执行。
-
变量自增操作的顺序:
num++
是后缀自增,先传递当前值,再自增。++num
是前缀自增,先自增,再传递新值。