Promise 练习
同步优先:Promise 构造函数、async函数内await之前的代码均为同步执行;微/宏任务优先级:微任务(后续代码)> 宏任务(setTimeout状态不可变:Promise 状态一旦变为fulfilledrejected,就不会再改变;本质await是的语法糖,async函数返回值自动包装为 Promise;Promise 链传递then/catch会创建新 Promise,其状态依赖原
在上一篇博客里,讲解了
Promise
的核心逻辑——从“pending/fulfilled/rejected”
的状态流转,到then/catch/finally
的链式调用,再到Promise.all
等静态方法的场景化使用等等。这一篇是对 Promise 检验和巩固。
上一篇文章👉Promise学习链接
题目 1
const promise = new Promise((resolve, reject) => {
console.log(1);
resolve(1);
console.log(2);
})
promise.then((res) => {
console.log(3);
})
console.log(4);
代码执行分析
- Promise构造函数中的代码立即执行
const promise = new Promise((resolve, reject) => {
console.log(1); // 立即执行,输出 1
resolve(1); // 将promise状态改为resolved
console.log(2); // 立即执行,输出 2
})
- then回调被添加到微任务队列
promise.then((res) => {
console.log(3); // 这个回调被添加到微任务队列
})
-
同步代码继续执行
执行console.log(5)
,输出5
(同步代码优先于所有异步任务)。 -
事件循环处理微任务队列
执行微任务队列中的 console.log(3)
题目关键点
● Promise
构造函数中的executor
函数是同步执行的,不是微任务
● 只有通过 then、catch、finally
注册的回调才是微任务
● 微任务会在同步代码执行完毕后才执行
● 所以执行顺序是:同步代码(1,2,4) → 微任务(3)
题目 2
const promise = new Promise((resolve, reject) => {
console.log(1);
setTimeout(() => {
console.log(2);
resolve();
console.log(3);
}, 1000);
});
promise.then(() => {
console.log(4);
});
console.log(5);
代码执行分析
-
Promise 创建阶段
new Promise(...)
时,构造函数内的代码会立即同步执行:- 执行
console.log(1)
,输出1
; setTimeout
被添加到宏任务队列,延迟 1 秒后执行;- 此时 Promise 处于
pending
状态。
- 执行
-
then 回调注册(等待状态)
调用promise.then(...)
时,由于 Promise 仍为pending
,then
回调不会立即执行,而是被暂存,等待 Promise 状态变为fulfilled
后触发。 -
同步代码执行
执行console.log(5)
,输出5
(同步代码优先于所有异步任务)。 -
宏任务执行(1 秒后)
延迟结束后,setTimeout
回调从宏任务队列取出执行:- 执行
console.log(2)
,输出2
; - 调用
resolve()
,将 Promise 状态改为fulfilled
; - 执行
console.log(3)
,输出3
(resolve
后仍会执行后续同步代码)。
- 执行
-
微任务执行(当前事件循环末尾)
Promise 状态变更后,暂存的then
回调被加入微任务队列,在宏任务执行完毕后立即执行:- 执行
console.log(4)
,输出4
。
- 执行
最终输出顺序
1
5
2
3
4
关键概念
- 宏任务(如
setTimeout
)需等待当前事件循环的同步代码、微任务执行完毕后才执行; - 微任务(如
Promise.then
)优先级高于宏任务,在同步代码结束后、宏任务开始前执行。
题目3
const promise1 = new Promise((resolve, reject) => {
setTimeout(() => {
resolve();
}, 1000);
});
const promise2 = promise1.catch(() => {
return 2;
});
console.log('promise1', promise1);
console.log('promise2', promise2);
setTimeout(() => {
console.log('promise1', promise1);
console.log('promise2', promise2);
}, 2000);
代码执行分析
-
promise1 创建(初始状态)
new Promise(...)
时,setTimeout
被加入宏任务队列,1 秒后执行resolve()
;
此时 promise1 处于pending
状态。 -
promise2 创建(依赖 promise1 状态)
promise1.catch(...)
会创建一个新的 Promise(promise2):catch
仅在promise1
状态变为rejected
时触发;- 由于
promise1
最终会resolve
,catch
回调不会执行; - 此时
promise2
同样处于pending
状态,等待 promise1 的状态结果。
-
同步输出(初始状态打印)
执行console.log('promise1', promise1)
和console.log('promise2', promise2)
,两者均输出Promise {<pending>}
。 -
宏任务 1 执行(1 秒后)
setTimeout
回调执行resolve()
,promise1 状态变为fulfilled
;
由于promise1
未被拒绝,promise2
会“继承”promise1 的状态,也变为fulfilled
(值为undefined
,因catch
未触发)。 -
宏任务 2 执行(2 秒后)
再次打印两个 Promise 的状态,均输出Promise {<fulfilled>: undefined}
。
最终输出顺序
promise1 Promise {<pending>}
promise2 Promise {<pending>}
promise1 Promise {<fulfilled>: undefined}
promise2 Promise {<fulfilled>: undefined}
关键概念
catch
是then(null, onRejected)
的语法糖,仅在原 Promise 被拒绝时触发;- 由
then/catch
创建的新 Promise,其状态依赖原 Promise 的状态:原 Promise 成功则新 Promise 也成功,原 Promise 失败则执行回调后新 Promise 可能成功(若回调返回正常值); - Promise 状态一旦确定(
fulfilled
/rejected
),就会永久不变。
题目 4
async function m1() {
return 1;
}
async function m2() {
const n = await m1();
console.log(n);
return 2;
}
async function m3() {
const n = m2();
console.log(n);
return 3;
}
m3().then((n) => {
console.log(n);
});
m3();
console.log(4);
代码执行分析
-
函数定义阶段
定义m1
/m2
/m3
三个async
函数,此时仅声明,未执行。 -
第一个 m3() 执行(同步优先)
- 进入
m3
,调用m2()
m2()
是async
函数,立即返回一个pending
状态的Promise(未执行内部逻辑),将m2
内部代码(含await m1()
)暂存;- 执行
console.log(n)
,输出[object Promise]
; m3
执行到return 3
,返回一个pending Promise
(因m2
未完成),并为其注册then
回调。
- 进入
-
第二个 m3() 执行(重复同步逻辑)
- 再次调用
m3()
,重复上述步骤:调用m2()
得到pending Promise
,执行console.log(n)
输出[object Promise]
,返回pending Promise
(无then
回调)。
- 再次调用
-
同步代码
执行console.log(4)
,输出4
。 -
微任务执行(异步队列处理)
同步代码结束后,处理暂存的async
函数内部逻辑:- 第一个 m2() 执行:调用
m1()
(async
函数返回Promise.resolve(1)
),await
等待后执行console.log(n)
输出1
,m2
返回Promise.resolve(2)
; - 第一个
m3
的 Promise 变为fulfilled
(值为 3),触发then
回调,输出3
; - 第二个 m2() 执行:同理,调用
m1()
后执行console.log(n)
输出1
,m2
返回Promise.resolve(2)
; - 第二个
m3
的 Promise 变为fulfilled
(值为 3),无then
回调,不输出。
- 第一个 m2() 执行:调用
最终输出顺序
[object Promise] // 第一个m3()中打印m2()返回的Promise
[object Promise] // 第二个m3()中打印m2()返回的Promise
4 // 同步代码console.log(4)
1 // 第一个m2()中打印await m1()的结果
3 // 第一个m3()的then回调打印结果
1 // 第二个m2()中打印await m1()的结果
关键概念
- 调用
async
函数时,先同步执行其内部代码,直到遇到第一个await
- 遇到
await
时,会暂停当前async
函数执行,返回一个 pending 状态的 Promise, await 右侧的表达式会被执行 await
后续的代码会被放入微任务队列,等待await
右侧的 Promise 完成后再执行async
函数的返回值会自动包装为Promise.resolve(返回值)
,即使返回普通值。
题目 5
var a;
var b = new Promise((resolve, reject) => {
console.log("promise1");
setTimeout(() => {
resolve();
}, 1000);
})
.then(() => {
console.log("promise2");
})
.then(() => {
console.log("promise3");
})
.then(() => {
console.log("promise4");
});
a = new Promise(async (resolve, reject) => {
console.log(a);
await b;
console.log(a);
console.log("after1");
await a;
resolve(true);
console.log("after2");
});
console.log("end");
代码执行分析
-
变量声明与 b 初始化
- 声明
a
(初始值undefined
); - 创建
b
:Promise 构造函数同步执行console.log("promise1")
,输出promise1
;setTimeout
加入宏任务队列(1 秒后resolve
); - 为
b
注册 3 个then
回调,均暂存(等待b
状态变更),此时b
为pending
。
- 声明
-
a 初始化(同步+异步暂停)
- 创建
a
的 Promise 时,构造函数内代码同步执行:- 打印
a
(此时a
未完成赋值,输出undefined
); - 遇到
await b
:暂停构造函数执行,等待b
完成;将后续代码(console.log(a)
及之后)暂存;
- 打印
- 完成
a
赋值,此时a
为pending
状态。
- 创建
-
同步代码收尾
执行console.log("end")
,输出end
。 -
宏任务执行(1 秒后)
setTimeout
回调执行resolve()
,b
状态变为fulfilled
,触发其then
链:- 第一个
then
执行console.log("promise2")
,输出2
; - 第二个
then
执行console.log("promise3")
,输出3
; - 第三个
then
执行console.log("promise4")
,输出4
; b
整个链条执行完毕,状态最终为fulfilled
。
- 第一个
-
a 的后续代码执行(微任务)
await b
等待结束,执行暂存的代码:- 打印
a
(此时a
是pending
Promise,输出[object Promise]
); - 打印
after1
,输出after1
; - 遇到
await a
:等待a
自身完成,但a
正处于pending
(依赖await a
的结果),形成循环等待; resolve(true)
及console.log("after2")
永远不会执行(a
永久pending
)。
- 打印
最终输出顺序
promise1
undefined
end
promise2
promise3
promise4
[object Promise]
after1
关键概念
- b 代表的是整个 Promise 链的最终结果,而非初始 Promise 的结果。
await b
会等待所有.then()
回调依次执行完毕,直到整个链条的最后一个Promise
完成。- 循环等待(如
await a
且a
依赖自身)会导致 Promise 永久pending
,后续代码无法执行;
题目 6
async function async1() {
console.log("async1 start");
await async2();
console.log("async1 end");
}
async function async2() {
console.log("async2");
}
console.log("script start");
setTimeout(function () {
console.log("setTimeout");
}, 0);
async1();
new Promise(function (resolve) {
console.log("promise1");
resolve();
}).then(function () {
console.log("promise2");
});
console.log("script end");
代码执行分析
-
同步代码
执行console.log("script start")
,输出script start
;setTimeout
加入宏任务队列(延迟 0 秒,仍需等待事件循环)。 -
async1 执行(同步+暂停)
- 调用
async1()
,同步执行console.log("async1 start")
,输出async1 start
; - 调用
async2()
,同步执行console.log("async2")
,输出async2
;async2
无await
,返回Promise.resolve(undefined)
; - 遇到
await async2()
:暂停async1
,将console.log("async1 end")
加入微任务队列;async1
返回 pending Promise。
- 调用
-
Promise 与同步代码收尾
- 执行
new Promise(...)
:构造函数同步执行console.log("promise1")
,输出promise1
;调用resolve()
,将then
回调(console.log("promise2")
)加入微任务队列; - 执行
console.log("script end")
,输出script end
。
- 执行
-
微任务执行(同步代码结束后)
微任务队列中有两个任务,按入队顺序执行:- 先执行
promise2
的then
回调,输出promise2
; - 再执行
async1
暂存的console.log("async1 end")
,输出async1 end
。
- 先执行
-
宏任务执行(微任务清空后)
执行setTimeout
回调,输出setTimeout
。
最终输出顺序
script start
async1 start
async2
promise1
script end
promise2
async1 end
setTimeout
关键概念
await
后的代码会被加入微任务队列,而非宏任务;- 微任务队列按“先入先出”执行,
promise2
的then
比async1 end
先入队,因此优先执行; - 即使
setTimeout
延迟为 0,仍需等待微任务队列清空后才执行(宏任务优先级低于微任务)。
总结
- 同步优先:Promise 构造函数、
async
函数内await
之前的代码均为同步执行; - 微/宏任务优先级:微任务(
then/catch/await
后续代码)> 宏任务(setTimeout
); - 状态不可变:Promise 状态一旦变为
fulfilled
/rejected
,就不会再改变; async/await
本质:await
是Promise.then
的语法糖,async
函数返回值自动包装为 Promise;- Promise 链传递:
then/catch
会创建新 Promise,其状态依赖原 Promise 及回调执行结果。
更多推荐
所有评论(0)