You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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方式执行)
  • 因此这段代码应该会产生死锁

实际运行输出

  1. p1 setTimeout
  2. p2 setTimeout
  3. Started
  4. P1
  5. P2
  6. End
  7. (2秒后)P2 => 36
  8. (10秒后)P1 => 17

疑问

第7、8行的输出是如何产生的?


解析

先纠正几个理解误区,再拆解执行流程:

  1. Promise构造函数是同步执行的:代码运行时会先立刻执行p1和p2的构造函数,打印p1 setTimeout和p2 setTimeout,同时把两个定时器回调交给浏览器定时器模块调度(不是直接进普通回调队列)。
  2. 同步任务优先执行:接着执行checkIt(),同步打印Started、P1、P2、End,这部分会一次性执行完毕,不会被异步任务打断。
  3. 异步任务的调度逻辑:
    • 定时器回调会在指定延迟时间到后,被放入宏任务队列。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到期更早所以先执行。
  • 这里不存在死锁:Promise的then回调只有在Promise状态变为resolved后才会进入微任务队列,而状态变化由定时器回调触发,定时器到点就会执行,整个流程是顺畅的。

内容的提问来源于stack exchange,提问作者Saad Ali

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 16:22:38