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

含异步操作的JavaScript程序输出不符预期的原因排查

你的JavaScript代码执行顺序不符合预期?来梳理下事件循环的细节

先把你提供的代码贴出来方便分析:

setTimeout(function() { console.log("Callbcack"); }, 0); 
new Promise((resolve, reject) => { 
    console.log("Inside promise"); 
    let ms = 5000 + new Date().getTime(); 
    while (new Date() < ms) {} 
    resolve("Promise is resolved"); 
}).then((data) => {console.log(data)}).catch((err) => { console.log(err); }) 
var ms = 2000 + new Date().getTime(); 
while (new Date() < ms) {}

你的预期和实际不符,核心是忽略了三个关键的事件循环和JavaScript执行机制细节:

1. Promise的executor函数是同步执行的

你误以为Promise的resolve是异步触发的,但实际上new Promise()的回调(executor)会在创建Promise的当下立即同步执行。所以代码执行到这里时,会先打印Inside promise,然后直接进入5秒的while循环——这会完全阻塞主线程,在这5秒里,浏览器什么都做不了:定时器的回调没法被触发,UI没法更新,所有异步任务都得等主线程空闲。

2. 事件循环的优先级:微任务 > 宏任务

JavaScript的事件循环执行顺序是固定的:

  • 先执行完当前所有同步任务(也就是主线程上的代码)
  • 然后清空微任务队列(比如Promise的then/catch/finally回调、MutationObserver回调等)
  • 最后才会从宏任务队列(比如setTimeout/setInterval回调、DOM事件回调等)中取出一个任务执行

在你的代码里,当executor执行完resolve("Promise is resolved")后,Promise的then回调会被加入到微任务队列。而setTimeout的回调是被加入到宏任务队列。当所有同步代码(两个while循环,总共7秒)执行完后,主线程会先处理微任务队列,所以先打印Promise is resolved,之后才会处理宏任务队列里的setTimeout回调,打印Callbcack。

3. setTimeout的"0延迟"不是真的立即执行

浏览器对setTimeout有最小延迟限制(一般是4ms),但更关键的是:即使你设置0延迟,回调也得等主线程空闲后才能执行。在你的代码里,主线程被两个while循环阻塞了7秒,所以setTimeout的回调根本不可能在2秒后触发——它得等这7秒的同步代码全部跑完,才会有机会执行。

完整的执行时序梳理

  1. 执行setTimeout:将回调加入宏任务队列,标记"最早0ms后执行",但此时主线程忙,暂时无法处理。
  2. 执行new Promise:立即同步执行executor函数:
    • 打印Inside promise
    • 执行5秒的while循环,阻塞主线程
    • 调用resolve,将then回调加入微任务队列
  3. 执行后面的2秒while循环,继续阻塞主线程
  4. 所有同步任务执行完毕,主线程开始处理微任务队列:
    • 执行Promise的then回调,打印Promise is resolved
  5. 微任务队列清空,主线程处理宏任务队列:
    • 执行setTimeout的回调,打印Callbcack

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 10:58:15