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

fake-timers结合真实HTTP请求时异步代码等待问题求助

fake-timers 结合真实HTTP请求时的异步等待问题

问题场景

我需要将fake-timers模拟的离散时间事件与真实HTTP请求结合使用,示例代码如下:

const FakeTimers = require("@sinonjs/fake-timers");
const clock = FakeTimers.install();

const axios = require('axios');

let fun = async () => {
    setTimeout(async function () {
        console.log(+new Date()+' Timer for 1000ms fired');
        await axios.get('https://jsonplaceholder.typicode.com/todos/1');
        console.log(+new Date() + ' Response');
    }, 1000);

    setTimeout(function () {
        console.log(+new Date()+' Timer for 2000ms fired');
       
    }, 2000);

    // Tick the clock to 1000ms and wait for any operations to finish.
    await clock.tickAsync(1000);

    // Tick the clock to 2000ms and wait for any operations to finish.
    await clock.tickAsync(1000);
}
fun();

预期输出

1000 Timer for 1000ms fired
1000 Response
2000 Timer for 2000ms fired

实际输出

1000 Timer for 1000ms fired
2000 Timer for 2000ms fired
2000 Response

想知道有没有办法让fake-timers等待真实异步代码(比如HTTP请求)执行完成后,再推进时间?

问题原因

fake-timers 的 tickAsync 只会等待它自身模拟的异步操作(比如模拟的 setTimeout、Promise.resolve 等),但真实的HTTP请求是由Node.js/浏览器的原生事件循环处理的,不属于fake-timers的模拟体系,所以tickAsync不会等待HTTP请求完成,直接推进了时间,导致第二个定时器先触发,之后HTTP响应才回来。

解决方案

核心思路是手动追踪真实异步操作的状态,等它完成后再推进fake-timers的时间。

方案1:追踪HTTP请求的Promise

修改代码,把第一个定时器里的HTTP请求Promise暴露出来,在tickAsync(1000)之后等待这个Promise完成,再继续推进时间:

const FakeTimers = require("@sinonjs/fake-timers");
const clock = FakeTimers.install();

const axios = require('axios');

let fun = async () => {
    let requestPromise; // 用于存储HTTP请求的Promise

    setTimeout(async function () {
        console.log(+new Date()+' Timer for 1000ms fired');
        requestPromise = axios.get('https://jsonplaceholder.typicode.com/todos/1');
        await requestPromise;
        console.log(+new Date() + ' Response');
    }, 1000);

    setTimeout(function () {
        console.log(+new Date()+' Timer for 2000ms fired');
       
    }, 2000);

    // 推进到1000ms,触发第一个定时器
    await clock.tickAsync(1000);
    // 等待真实HTTP请求完成
    await requestPromise;
    // 再推进1000ms到2000ms
    await clock.tickAsync(1000);
}
fun();

方案2:使用clock.runAllAsync配合信号量

如果有多个异步操作需要等待,可以用一个信号量来标记所有真实异步操作完成,再停止fake-timers的运行:

const FakeTimers = require("@sinonjs/fake-timers");
const clock = FakeTimers.install();

const axios = require('axios');

let fun = async () => {
    let isRequestDone = false;

    setTimeout(async function () {
        console.log(+new Date()+' Timer for 1000ms fired');
        await axios.get('https://jsonplaceholder.typicode.com/todos/1');
        console.log(+new Date() + ' Response');
        isRequestDone = true;
    }, 1000);

    setTimeout(function () {
        // 只有当HTTP请求完成后才触发这个定时器的逻辑
        if (!isRequestDone) {
            // 重新安排定时器,直到请求完成
            setTimeout(this, 100);
            return;
        }
        console.log(+new Date()+' Timer for 2000ms fired');
    }, 2000);

    // 运行所有模拟的定时器,直到没有待处理的模拟任务
    await clock.runAllAsync();
}
fun();

说明

两种方案中,方案1更直接,适合单个明确的异步操作;方案2适合复杂场景,通过重新调度模拟定时器来等待真实异步操作完成。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 16:33:12