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

为何含0ms setTimeout的宏任务队列优先级看似高于微任务队列?

问题

我原本认为,当宏任务队列和微任务队列中都有就绪待执行的任务时,微任务拥有更高的优先级。我通过让两个队列都填满任务的方式进行代码测试,验证该规则是否成立,但发现0ms的setTimeout并非如此——它总是先执行。不过如果我添加Promise.resolve('Promise').then(displayData)这段代码,该微任务会先于0ms的setTimeout执行。以下是测试代码及输出,请求解释该现象:

// after 3000ms:
// task queue:printFoo(0ms), printHi(1ms), PrintHello(2ms)
// microtask queue: displayData (about 50ms)

function printHello() {
  console.log("hello");
}

function printHi() {
  console.log("hi");
}

function printFoo() {
  console.log("foo");
}

function displayData(data) {
  console.log("data", data);
}

function sleep(miliseconds) {
  var currentTime = new Date().getTime();
  while (currentTime + miliseconds >= new Date().getTime()) {}
}

setTimeout(printHi, 1);
setTimeout(printHello, 2);
setTimeout(printFoo, 0);
fetch("https://jsonplaceholder.typicode.com/todos/1").then(displayData); // ~~ 50ms
// Promise.resolve('Promise').then(displayData);
sleep(3000);

输出:

foo
data from fetch
hi
hello
原因分析
  • 微任务优先级高于宏任务的核心前提是:微任务已经被加入微任务队列,和宏任务处于同等“就绪待执行”状态。
  • 你的测试代码里,fetch的回调依赖网络响应:虽然sleep(3000)阻塞主线程的过程中,网络请求可能已经完成,但主线程被阻塞时,浏览器无法处理网络响应的回调逻辑,所以displayData直到sleep结束后才会被加入微任务队列。
  • 而三个setTimeout的回调在各自延迟时间(0ms、1ms、2ms)到期后,会被准时加入宏任务队列——哪怕主线程被阻塞,宏任务队列依然会接收这些任务,只是暂时无法执行。
  • 当sleep结束,主线程处理任务的流程是:
    1. 先从宏任务队列取出最早就绪的printFoo(0ms延迟的setTimeout)执行,输出foo。
    2. 执行完这个宏任务后,主线程会强制清空所有微任务队列——这时候浏览器才处理完fetch的响应,把displayData加入了队列,所以立刻执行它,输出data from fetch。
    3. 微任务全部执行完毕后,再回到宏任务队列,依次执行printHi和printHello,输出hi和hello。
  • 换成Promise.resolve('Promise').then(displayData)时,这个微任务会在代码执行到该行时直接被加入微任务队列,不需要等待任何异步操作。当sleep结束后,主线程会先清空微任务队列(执行displayData),再去处理宏任务队列里的setTimeout回调,所以微任务会先执行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 12:25:32