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

