如何在事件触发时终止函数进程?现有方案问题分析
如何通过事件触发立即终止异步函数进程?
我需要在事件触发时终止异步函数进程。在函数内部抛出错误(例如在任意步骤的delay中抛错)可以正常终止进程,但如果通过事件监听器来终止进程,在监听器内抛错会导致进程继续运行,且错误会被视为Unhandled Rejection或DOMException——这是因为错误是在监听器上下文而非主函数上下文抛出的,无法影响主进程。
以下是我尝试的几种方案及存在的问题:
- 在事件监听器内抛错终止进程:进程仍正常运行,错误被标记为
Unhandled Rejection或DOMException
(() => { class EventEmitter { constructor() { this.events = {}; } on(event, listener) { if (!this.events[event]) { this.events[event] = []; } this.events[event].push(listener); // Return a function that can be used to remove this specific listener return () => { this.off(event, listener); }; } emit(event, ...args) { if (this.events[event]) { this.events[event].forEach(listener => listener(...args)); } } removeListeners(event) { delete this.events[event]; } off(event, listener) { if (this.events[event]) { this.events[event] = this.events[event].filter(existingListener => existingListener !== listener); } } } const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms)); const processEmitter = new EventEmitter(); async function startProcess(params) { try { const removeListener = processEmitter.on('stop', async () => { removeListener(); throw new Error(`Process stopped due to external event`); }); await delay(2000); console.log('finished step 1'); await delay(2000); console.log('finished step 2'); await delay(2000); console.log('finished step 3'); await delay(2000); console.log('finished step 4'); await delay(2000); console.log('finished step 5'); await delay(2000); console.log('finished step 6'); } catch (err) { console.log('Process terminated:', err.message); } finally { console.log('done !!') } } startProcess(); setTimeout(() => { processEmitter.emit('stop'); }, 5000); })();
- 用Proxy在值变更时抛错:仍无法终止进程
(() => { const processEmitter = new EventEmitter(); async function startProcess(params) { try { // use proxy as a middleware when the value change const abortHandler = new Proxy({ msg: '' }, { set(target, key, value) { target[key] = value; throw new Error(value); } }); const removeListener = processEmitter.on('stop', async () => { removeListener(); abortHandler.msg = `Process stopped due to external event`; }); await delay(2000); console.log('finished step 1'); await delay(2000); console.log('finished step 2'); await delay(2000); console.log('finished step 3'); await delay(2000); console.log('finished step 4'); await delay(2000); console.log('finished step 5'); await delay(2000); console.log('finished step 6'); } catch (err) { console.log('Process terminated:', err.message); } finally { console.log('done !!') } } startProcess(); setTimeout(() => { processEmitter.emit('stop'); }, 5000); })();
- 用自定义reject函数抛错:因上下文问题失败
(() => { const processEmitter = new EventEmitter(); async function startProcess(params) { try { // use proxy as a middleware when the value change const reject = msg => { throw new Error(msg); } const removeListener = processEmitter.on('stop', async () => { removeListener(); reject(`Process stopped due to external event`); }); await delay(2000); console.log('finished step 1'); await delay(2000); console.log('finished step 2'); await delay(2000); console.log('finished step 3'); await delay(2000); console.log('finished step 4'); await delay(2000); console.log('finished step 5'); await delay(2000); console.log('finished step 6'); } catch (err) { console.log('Process terminated:', err.message); } finally { console.log('done !!') } } startProcess(); setTimeout(() => { processEmitter.emit('stop'); }, 5000); })();
- 用Promise包裹并调用reject:进程仍继续运行
(() => { const processEmitter = new EventEmitter(); async function startProcess(params) { return new Promise(async (_resolve, reject) => { try { const removeListener = processEmitter.on('stop', async () => { removeListener(); reject(`Process stopped due to external event`); }); await delay(2000); console.log('finished step 1'); await delay(2000); console.log('finished step 2'); await delay(2000); console.log('finished step 3'); await delay(2000); console.log('finished step 4'); await delay(2000); console.log('finished step 5'); await delay(2000); console.log('finished step 6'); } catch (err) { console.log('Process terminated:', err.message); } finally { console.log('done !!') } }) } startProcess(); setTimeout(() => { processEmitter.emit('stop'); }, 5000); })();
- 使用
AbortController或标记变量:存在代码冗余且无法立即终止当前执行步骤
(() => { const delay = (ms, signal) => { if (signal?.aborted) { throw new Error(signal?.reason || 'Aborted') } return new Promise(resolve => setTimeout(resolve, ms)) }; const processEmitter = new EventEmitter(); const controller = new AbortController(); const { signal } = controller; async function startProcess(params) { try { const removeListener = processEmitter.on('stop', async () => { removeListener(); controller.abort('Process stopped due to external event'); }); await delay(2000, signal); console.log('finished step 1'); await delay(2000, signal); console.log('finished step 2'); await delay(2000, signal); console.log('finished step 3'); await delay(2000, signal); console.log('finished step 4'); await delay(2000, signal); console.log('finished step 5'); await delay(2000, signal); console.log('finished step 6'); } catch (err) { console.log('Process terminated:', err.message); } finally { console.log('done !!') } } startProcess(); setTimeout(() => { processEmitter.emit('stop'); }, 5000); })();
(() => { const delay = (ms, err) => { if (err) { throw err; } return new Promise(resolve => setTimeout(resolve, ms)) }; const processEmitter = new EventEmitter(); async function startProcess(params) { try { let err = null; const removeListener = processEmitter.on('stop', async () => { removeListener(); err = new Error('Process stopped due to external event'); }); await delay(2000, err); console.log('finished step 1'); await delay(2000, err); console.log('finished step 2'); await delay(2000, err); console.log('finished step 3'); await delay(2000, err); console.log('finished step 4'); await delay(2000, err); console.log('finished step 5'); await delay(2000, err); console.log('finished step 6'); } catch (err) { console.log('Process terminated:', err.message); } finally { console.log('done !!') } } startProcess(); setTimeout(() => { processEmitter.emit('stop'); }, 5000); })();
我希望找到一种无需冗余代码、能立即终止进程的解决方案。
内容来源于Stack Exchange,提问作者Mahmoud Y3c
相关产品推荐
相关产品推荐

