JavaScript异步测试中模拟延迟的核心是用Jest假定时器快进时间而非真实等待:调用jest.useFakeTimers()替换全局定时器,jest.advanceTimersByTime()推进时间,需配合waitFor等待微任务,结束后调用jest.useRealTimers()。

在 JavaScript 异步代码测试中,模拟延迟时间的核心是**不真实等待**,而是用假定时器(Fake Timers)让时间“快进”,从而快速验证逻辑是否按时触发、状态是否正确更新。
用 Jest 的假定时器接管 setTimeout
Jest 内置了完整的假定时器支持,无需额外库:
- 测试开始前调用 jest.useFakeTimers(),它会替换全局的
setTimeout、setInterval、clearTimeout等函数 - 被测代码里调用的
setTimeout(fn, 1000)不会真等 1 秒,而是被记录下来,暂不执行 - 用 jest.advanceTimersByTime(1000) 模拟过去 1000ms,此时所有已注册且到期的定时器立即执行
- 测试结束后务必调用 jest.useRealTimers(),避免影响后续测试
区分宏任务和微任务
假定时器只控制宏任务(如 setTimeout),对 Promise 微任务(.then、await)无影响:
- 如果异步逻辑是
setTimeout(() => fetch(...).then(updateState), 500),推进时间后需再 await waitFor(() => expect(...).toBe(...)) 等待 Promise 完成和状态更新 - 不能只靠
advanceTimersByTime(500)就断言最终 UI 或数据状态,必须显式等待微任务链结束 - 简单场景下可配合 await new Promise(resolve => setTimeout(resolve, 0)) 手动清空微任务队列(但推荐用
waitFor)
处理复杂定时器行为
实际代码中常有清除、重复、嵌套等逻辑,假定时器能准确反映这些行为:
立即学习“Java免费学习笔记(深入)”;
- 被
clearTimeout(id)取消的定时器,调用advanceTimersByTime时不会执行 - 用 jest.getTimerCount() 断言剩余未触发的定时器数量,验证清理是否彻底
- 需要逐个执行(比如测试轮询或链式调用),可用 jest.runOnlyPendingTimers() 只运行队列中最前面一个
- 想一次性清空所有待执行定时器(不管是否到期),用 jest.runAllTimers()
替代方案:手动 Promise 延迟工具
对于轻量测试或非 Jest 环境,可封装一个返回 Promise 的延迟函数:
- const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
- 在
async测试中 await delay(500),比假定时器更直观,但会真实等待,不适合高频或长延时测试 - 适合验证 Promise 链、错误传播、
AbortSignal中断等不依赖定时器 API 的逻辑


















