Jest测试JS Promise时序问题:fakeTimers下重试超时逻辑未触发如何解决?
问题1 执行顺序原因
这个现象确实和await的调度逻辑直接相关。await是Promise的语法糖,会把后续代码封装为当前Promise的.then()回调,所有Promise回调都会进入微任务队列,等待当前同步执行栈完全清空后才会按入队顺序执行。
你遇到的顺序错位,是因为async函数的await回调在入队时,会比当前同步流程中直接调用Promise.resolve().then()产生的回调多占一个微任务周期:你在触发connect事件同步调用accept()之后,被测代码里await后的setTimeout注册逻辑确实进入了微任务队列,但你测试代码里的runAllTimers回调是后续同步逻辑中直接加入队列的,反而排在了await回调的前面,导致定时器还没注册就被执行了清空操作。
问题2 无真实定时器的解决方案
核心思路是等待所有微任务执行完毕,保证被测代码的setTimeout已经完成注册,再执行jest.runAllTimers(),有两种可靠方案:
方案1:使用jest内置的微任务清空方法
jest专门提供了jest.runAllTicks()用来清空当前所有微任务,不需要自己嵌套Promise,兼容性最好:
err = { toString: () => 'testing' } connection.emit('connect', err); // 先清空所有微任务,确保被测代码的setTimeout已经注册 jest.runAllTicks(); // 再执行所有定时器逻辑 jest.runAllTimers();
方案2:嵌套两层Promise等待微任务执行
如果因为jest版本限制无法用runAllTicks,可以多等一轮微任务保证被测逻辑执行完毕:
err = { toString: () => 'testing' } connection.emit('connect', err); // 连续等待两轮微任务,覆盖await的调度延迟 Promise.resolve() .then(() => Promise.resolve()) .then(() => { jest.runAllTimers(); });
两种方案都不需要用到真实定时器,就能保证定时器注册完成后再执行快进操作。
内容的提问来源于stack exchange,提问作者akc42
相关产品推荐
相关产品推荐

