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

Promise.race无法终止长耗时任务及传函数执行异常咨询

问题解决与分析

问题1:timeoutPromise提前触发的原因与修复

你当前的timeoutPromise在脚本加载阶段就直接完成了实例化,定时器从那一刻开始计时,完全不受executeWithRace调用时机的控制。比如脚本加载后过了70秒才调用executeWithRace,此时timeoutPromise已经处于resolved状态,Promise.race会直接返回它的结果,完全起不到超时控制的作用。

修复方案是把timeoutPromise和testPromise都改成工厂函数,每次调用executeWithRace时才创建新的Promise实例,重新启动定时器并执行test:

async function executeWithRace() {
    // 每次调用时才生成新的Promise实例
    const response = await Promise.race([createTimeoutPromise(), createTestPromise()]);
    console.log(response);
}

// 封装成函数,调用时才创建Promise并启动定时器
function createTimeoutPromise() {
    return new Promise((resolve) => {
        setTimeout(() => {
            resolve('Timed Out');
        }, 60000); // 1分钟超时
    });
}

function createTestPromise() {
    return new Promise((resolve, reject) => {
        try {
            test();
            resolve("testSuccessfull");
        } catch (err) {
            reject(err); // 捕获test执行过程中的错误
        }
    });
}

问题2:封装函数传入Promise.race只执行第一个的原因

Promise.race的参数要求是Promise实例的可迭代对象,如果你直接传入函数数组(比如Promise.race([func1, func2])),Promise.race会把每个函数用Promise.resolve()包裹,而Promise.resolve(函数)会直接把函数本身作为resolved值,不会执行函数。此时race会立即完成并返回第一个函数对象,自然不会触发第二个函数的执行。

正确的做法是调用函数获取Promise实例后再传入race,也就是像上面代码里的Promise.race([createTimeoutPromise(), createTestPromise()])——这里是主动调用函数,得到Promise实例后再放进数组,这样两个Promise才会同时启动执行。

关键注意点:同步无限循环的特殊情况

如果你的test()是同步的无限循环,上面的Promise方案完全无效。因为同步代码会阻塞JavaScript的事件循环,定时器的回调根本没机会执行,timeoutPromise永远不会resolve,页面还是会崩溃。

这种情况下必须用Web Worker把test()放到独立线程执行,主线程可以通过定时器监听超时,然后终止Worker:

async function executeWithRace() {
    return new Promise((resolve, reject) => {
        // 创建Web Worker
        const worker = new Worker('test-worker.js');
        
        // 启动超时定时器
        const timeoutId = setTimeout(() => {
            worker.terminate(); // 终止Worker线程
            resolve('Timed Out');
        }, 60000);
        
        // 监听Worker执行完成的消息
        worker.onmessage = (e) => {
            clearTimeout(timeoutId);
            worker.terminate();
            resolve("testSuccessfull");
        };
        
        // 监听Worker执行错误
        worker.onerror = (err) => {
            clearTimeout(timeoutId);
            worker.terminate();
            reject(err);
        };
        
        // 发送指令让Worker执行test
        worker.postMessage('run-test');
    });
}

对应的test-worker.js内容:

self.onmessage = (e) => {
    if (e.data === 'run-test') {
        test(); // 在这里定义你的test函数
        self.postMessage('done');
    }
};

// 定义test函数
function test() {
    // 你的业务逻辑,包括可能的无限循环
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 21:46:28