Promise.race触发拒绝时取消其他Promise的执行方案
问题描述
我正在开发一款异步Node.js应用,部分逻辑用Promise.race实现超时控制,但遇到一个问题:processSource(ast)可能会无限运行,当前的超时逻辑只是停止等待,无法真正终止该函数的执行,想知道有没有可行的实现方案。
当前代码
async function callDeobfuscator(ast) { const withTimeout = (promise) => { let timeoutPid; const timeoutPromise = new Promise((resolve, reject) => timeoutPid = setTimeout(reject, timeout)); return Promise.race([ promise, timeoutPromise ]).finally(() => { if (timeoutPid) { clearTimeout(timeoutPid); } }); }; return withTimeout(processSource(ast)); };
可行解决方案
首先明确:Promise.race仅能终止对异步任务的等待,无法主动终止未完成的异步/同步执行逻辑。针对不同类型的processSource实现,有以下几种可行方案:
方案1:使用AbortController(适配支持中断的异步操作)
如果processSource内部调用了支持AbortSignal的API(如Node.js文件流、fetch、部分第三方库),可以通过AbortController传递中断信号,主动终止执行:
async function callDeobfuscator(ast) { const controller = new AbortController(); const timeoutPid = setTimeout(() => controller.abort(), timeout); try { // 假设processSource可接收AbortSignal参数 return await processSource(ast, { signal: controller.signal }); } finally { clearTimeout(timeoutPid); } };
需要同步修改processSource,在关键逻辑节点检查中断状态,或把信号传递给内部支持中断的操作:
async function processSource(ast, { signal }) { // 同步逻辑中定期检查中断 if (signal.aborted) throw new Error('操作已中断'); // 异步操作传递信号 await someAsyncTask(ast, { signal }); }
方案2:子进程隔离(适配CPU密集/无法中断的同步逻辑)
如果processSource是CPU密集型的同步代码(比如无限循环),单线程Node.js无法中断正在执行的同步函数,必须通过子进程隔离实现强制终止:
主进程代码
const { fork } = require('child_process'); const path = require('path'); async function callDeobfuscator(ast) { return new Promise((resolve, reject) => { const worker = fork(path.join(__dirname, 'deobfuscator-worker.js')); const timeoutPid = setTimeout(() => { worker.kill(); // 超时后强制杀死子进程 reject(new Error('处理超时')); }, timeout); worker.send(ast); worker.on('message', (result) => { clearTimeout(timeoutPid); worker.kill(); resolve(result); }); worker.on('error', (err) => { clearTimeout(timeoutPid); reject(err); }); worker.on('exit', (code) => { if (code !== 0 && timeoutPid) { reject(new Error(`子进程异常退出,代码:${code}`)); } }); }); };
子进程文件deobfuscator-worker.js
process.on('message', async (ast) => { try { const result = await processSource(ast); process.send(result); process.exit(0); } catch (err) { process.send({ error: err.message }); process.exit(1); } }); // 原processSource逻辑 async function processSource(ast) { // 你的业务代码 }
该方案通过进程隔离实现彻底终止,代价是进程间通信的少量性能开销。
方案3:Worker线程隔离(轻量级替代子进程)
如果不想用完整的子进程,可以用Node.js的worker_threads模块,实现轻量级线程隔离,终止逻辑和子进程类似:
主进程代码
const { Worker } = require('worker_threads'); const path = require('path'); async function callDeobfuscator(ast) { return new Promise((resolve, reject) => { const worker = new Worker(path.join(__dirname, 'deobfuscator-worker.js'), { workerData: ast }); const timeoutPid = setTimeout(() => { worker.terminate(); // 终止Worker线程 reject(new Error('处理超时')); }, timeout); worker.on('message', (result) => { clearTimeout(timeoutPid); worker.terminate(); resolve(result); }); worker.on('error', (err) => { clearTimeout(timeoutPid); reject(err); }); worker.on('exit', (code) => { if (code !== 0 && timeoutPid) { reject(new Error(`Worker线程异常退出,代码:${code}`)); } }); }); };
Worker线程文件deobfuscator-worker.js
const { parentPort, workerData } = require('worker_threads'); async function run() { try { const result = await processSource(workerData); parentPort.postMessage(result); } catch (err) { parentPort.postMessage({ error: err.message }); } } run(); async function processSource(ast) { // 你的业务代码 }
内容的提问来源于stack exchange,提问作者GRoutar
相关产品推荐
相关产品推荐

