JavaScript回调与微任务队列疑问:代码输出与理解不符原因
JavaScript Promise与定时器执行顺序解析
p1 = new Promise((res,rej)=>{ console.log("p1 setTimeout"); setTimeout(()=>{ res(17); }, 10000); }); p2 = new Promise((res,rej)=>{ console.log("p2 setTimeout"); setTimeout(()=>{ res(36); }, 2000); }); function checkIt() { console.log("Started"); let val1 = this.p1; console.log("P1"); val1.then((data)=>{console.log("P1 => "+data)}); let val2 = this.p2; console.log("P2"); val2.then((data)=>{console.log("P2 => "+data)}); console.log("End"); } checkIt();
我的理解
- 回调队列中,p2的setTimeout先入队,p1的setTimeout后入队(按FIFO方式执行)
- 回调队列不会在微任务队列之前执行
- 微任务队列中,p1的回调函数先入队,p2的回调函数后入队(按FIFO方式执行)
- 因此这段代码应该会产生死锁
实际运行输出
- p1 setTimeout
- p2 setTimeout
- Started
- P1
- P2
- End
- (2秒后)P2 => 36
- (10秒后)P1 => 17
疑问
第7、8行的输出是如何产生的?
解析
先纠正几个理解误区,再拆解执行流程:
- Promise构造函数是同步执行的:代码运行时会先立刻执行p1和p2的构造函数,打印
p1 setTimeout和p2 setTimeout,同时把两个定时器回调交给浏览器定时器模块调度(不是直接进普通回调队列)。 - 同步任务优先执行:接着执行
checkIt(),同步打印Started、P1、P2、End,这部分会一次性执行完毕,不会被异步任务打断。 - 异步任务的调度逻辑:
- 定时器回调会在指定延迟时间到后,被放入宏任务队列。p2的延迟是2秒,比p1的10秒早到期,所以先触发:执行
res(36)将p2的Promise状态改为resolved,随即把p2的then回调放入微任务队列。 - 宏任务执行完毕后,会清空当前所有微任务,因此执行p2的
then回调,打印P2 => 36。 - 10秒后p1的定时器到期,触发回调执行
res(17),将p1的Promise状态改为resolved,把p1的then回调放入微任务队列,宏任务执行完后清空微任务,打印P1 => 17。
- 定时器回调会在指定延迟时间到后,被放入宏任务队列。p2的延迟是2秒,比p1的10秒早到期,所以先触发:执行
另外你之前的错误点:
- 定时器回调不是按创建顺序入队,而是按延迟到期时间的先后调度,p2到期更早所以先执行。
- 这里不存在死锁:Promise的
then回调只有在Promise状态变为resolved后才会进入微任务队列,而状态变化由定时器回调触发,定时器到点就会执行,整个流程是顺畅的。
内容的提问来源于stack exchange,提问作者Saad Ali
相关产品推荐
相关产品推荐

