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

Jest 26中jest.runAllTicks未完全耗尽微任务队列问题

问题根因

你遇到的现象由两个核心机制共同导致:

1. 未被await的独立Promise链微任务排序

代码里B().then(F)没有被await,是和A返回值无关的独立Promise链。当同步代码执行到await A()这一行时,V8原生微任务队列的初始顺序是:

  • 队首:E回调,来自D().then(E),是最早注册的已resolve Promise的回调
  • 队中:test函数await A()之后的恢复逻辑,也就是jest.runAllTicks()、console.log(arr)这部分代码

同步执行栈清空后引擎开始按顺序跑微任务:

  1. 先执行E,往数组推入'E'。E返回Promise.resolve()后,引擎会往队列末尾追加一个新微任务:等E返回的Promise resolve完成,再把B()返回的Promise标记为resolve,之后将F注册为then回调入队。
  2. 此时微任务队列顺序变为:[test函数恢复逻辑, 触发F入队的处理逻辑]
  3. 接下来引擎执行test函数恢复逻辑:先调用jest.runAllTicks(),紧接着执行console.log(arr)。这时候F和X对应的回调还排在队列后面没执行,自然不会出现在日志里。等console.log打印完成后,引擎才会继续处理后续微任务执行F、X,但已经过了日志打印的时机。

2. Jest 26版本jest.runAllTicks()的能力边界

Jest 26的jest.useFakeTimers()默认不会mock全局Promise实现,jest.runAllTicks()只会清空Jest自己维护的mock微任务队列(比如被mock的process.nextTick、setImmediate回调),完全不会干涉V8引擎原生管理的Promise微任务队列,所以调用这个方法根本不会把排在队列后面的F、X相关微任务提前拉出来执行。

验证方法

如果在打印日志前额外等待两轮原生微任务,就能得到你预期的完整顺序:

test("test", async ()=>{
    jest.useFakeTimers();
    await A();
    jest.runAllTicks();
    // 等待两轮微任务消化B链上的回调
    await Promise.resolve();
    await Promise.resolve();
    console.log(arr); // 输出 [ 'A', 'B', 'D', 'C', 'E', 'F', 'X' ]
});

如果需要runAllTicks()真正递归清空所有微任务(包括Promise链执行过程中新入队的任务),建议升级到Jest 27及以上版本,新版本默认会接管Promise微任务队列,符合你对API行为的预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.01 12:27:27