性能下降可能原因
- 资源竞争:大量异步任务同时请求资源(如数据库连接、文件系统访问等),导致资源竞争,任务等待时间延长。
- 事件循环阻塞:超时控制和取消功能实现不当,可能导致事件循环阻塞。例如,频繁的定时器检查或复杂的取消逻辑处理。
- 内存开销:每个任务都设置超时和取消功能,可能会创建大量的定时器和状态管理对象,增加内存开销,导致垃圾回收压力增大。
优化策略一:使用Promise.race
和AbortController
- 原理:
Promise.race
可以在多个Promise中只要有一个完成就返回,结合AbortController
用于取消异步操作。通过这种方式可以更高效地管理超时和取消逻辑。
- 实现要点:
- 创建
AbortController
实例,获取signal
。
- 将异步任务封装为Promise,并在任务内部监听
signal
的abort
事件,若事件触发则抛出异常。
- 使用
Promise.race
将任务Promise和超时Promise进行竞争。
const { AbortController } = require('node:abort_controller');
function asyncTask(signal) {
return new Promise((resolve, reject) => {
const timeoutId = setTimeout(() => {
resolve('Task completed');
}, 1000);
signal.addEventListener('abort', () => {
clearTimeout(timeoutId);
reject(new Error('Task aborted'));
});
});
}
const controller = new AbortController();
const { signal } = controller;
Promise.race([
asyncTask(signal),
new Promise((_, reject) => {
setTimeout(() => {
reject(new Error('Task timed out'));
}, 500);
})
]).then(console.log).catch(console.error);
// 取消任务
controller.abort();
- 优点:实现相对简单,利用原生的
Promise
和AbortController
,性能较好,能有效管理超时和取消。
- 缺点:对于复杂的异步任务,需要仔细处理内部逻辑以响应
signal
的abort
事件。
优化策略二:利用队列和限流
- 原理:将异步任务放入队列中,通过限流控制同一时间执行的任务数量,减少资源竞争和事件循环阻塞。同时在队列任务执行时设置超时和取消逻辑。
- 实现要点:
- 创建任务队列和一个计数器记录正在执行的任务数量。
- 利用
async/await
控制任务按顺序从队列中取出执行。
- 为每个任务设置超时和取消逻辑。
class TaskQueue {
constructor(concurrency) {
this.queue = [];
this.running = 0;
this.concurrency = concurrency;
}
enqueue(task, timeout, controller) {
return new Promise((resolve, reject) => {
this.queue.push({ task, timeout, controller, resolve, reject });
this.processQueue();
});
}
async processQueue() {
while (this.running < this.concurrency && this.queue.length > 0) {
const { task, timeout, controller, resolve, reject } = this.queue.shift();
this.running++;
const timeoutId = setTimeout(() => {
controller.abort();
reject(new Error('Task timed out'));
}, timeout);
try {
const result = await task(controller.signal);
clearTimeout(timeoutId);
resolve(result);
} catch (error) {
clearTimeout(timeoutId);
reject(error);
} finally {
this.running--;
this.processQueue();
}
}
}
}
// 使用示例
const queue = new TaskQueue(5);
const controller = new AbortController();
queue.enqueue(() => asyncTask(controller.signal), 1000, controller)
.then(console.log).catch(console.error);
- 优点:有效控制资源竞争,提高系统稳定性,适用于高并发场景。可以灵活调整并发任务数量。
- 缺点:实现相对复杂,需要管理任务队列和并发控制逻辑。如果并发数设置不当,可能影响整体性能。