为何setTimeout内外通过Promise.race获取的Promise状态结果不同?
问题:为何不同时机调用getStates(),Promise状态检测结果有差异?
在MDN提供的Promise.race相关示例代码中,为何在setTimeout外部调用getStates()函数,与在setTimeout内部调用该函数时,p1、p2、p3这三个Promise的执行状态检测结果存在差异?原本以为所有Promise都会返回pendingState对象,但在setTimeout内的情况并非如此。
示例代码
const p1 = new Promise((res) => setTimeout(() => res(100), 100)); const p2 = new Promise((res) => setTimeout(() => res(200), 200)); const p3 = new Promise((res, rej) => setTimeout(() => rej(300), 100)); async function getStates() { console.log(await promiseState(p1)); console.log(await promiseState(p2)); console.log(await promiseState(p3)); } console.log("Immediately after initiation:"); getStates(); setTimeout(() => { console.log("After waiting for 100ms:"); getStates(); }, 100);
promiseState函数实现
function promiseState(promise) { const pendingState = { status: "pending" }; return Promise.race([promise, pendingState]).then( (value) => value === pendingState ? value : { status: "fulfilled", value }, (reason) => ({ status: "rejected", reason }), ); }
回答:
核心原因:Promise状态变化时机 + 事件循环机制
差异的本质是两个规则的叠加:
- Promise.race的执行逻辑:
Promise.race([A, B])会返回第一个进入"已敲定"状态的结果。如果参数里有非Promise值(比如这里的pendingState普通对象),会被直接包装成已resolve的Promise,值就是该对象。 - JavaScript事件循环的执行顺序:
- 初始化后立即调用getStates():此时p1、p2、p3内部的setTimeout回调都还没执行(setTimeout回调属于宏任务,要等当前同步代码跑完才会处理),三个Promise全处于pending状态。所以
Promise.race([pending的Promise, pendingState])会优先返回已resolve的pendingState,检测结果全是pending。 - 等待100ms后调用getStates():此时p1和p3的100ms setTimeout回调已经执行,p1被resolve为100,p3被reject为300,两者都完成了状态转换;但p2的200ms回调还没触发,依旧是pending。所以:
- 检测p1时,
Promise.race([已resolve的p1, pendingState])会返回p1的resolve值,最终输出{ status: "fulfilled", value: 100 } - 检测p3时,
Promise.race([已reject的p3, pendingState])会触发reject分支,输出{ status: "rejected", reason: 300 } - 检测p2时,还是返回
pendingState,输出{ status: "pending" }
- 检测p1时,
- 初始化后立即调用getStates():此时p1、p2、p3内部的setTimeout回调都还没执行(setTimeout回调属于宏任务,要等当前同步代码跑完才会处理),三个Promise全处于pending状态。所以
补充说明
Promise的状态一旦从pending变为fulfilled或reject就会永久固定,所以100ms后检测p1、p3时,它们已经完成状态转换,Promise.race自然会优先选择已敲定的Promise,而非作为普通值的pendingState。
内容的提问来源于stack exchange,提问作者Nic B
相关产品推荐
相关产品推荐

