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

如何实现长耗时函数后续调用需等待前次执行完毕再运行?

长耗时异步函数串行排队执行方案

最简正确实现

不需要引入第三方工具库,直接利用Promise链维护执行队列即可,无竞态问题,代码如下:

// 初始化队列起点,初始状态为已完成
let taskQueue = Promise.resolve();

/**
 * 包装后的排队执行函数
 * 调用后无需主动await,任务会自动按调用顺序串行执行
 */
function enqueueTask(...args) {
  // 将当前任务挂到队列末尾,前序任务执行完成(无论成功/失败)后自动启动当前任务
  taskQueue = taskQueue.finally(() => longRunningAsyncFunc(...args));
  // 若后续需要单独获取某次任务的结果/捕获错误,可直接return该Promise,无该需求可省略返回值
  return taskQueue;
}

方案逻辑说明

  • 完全匹配需求:所有调用严格遵循调用顺序串行执行,前一个任务未执行完毕时,后续调用不会启动,不会出现多实例并发运行的情况
  • 无竞态隐患:不需要手动维护isRunning类的布尔状态锁,Promise原生的状态流转机制天然保证执行顺序,高频调用下也不会出现锁判断失效的问题
  • 错误处理灵活:使用finally挂载任务时,前序任务抛出异常不会阻塞后续排队任务;如果需要前序任务失败就终止整个队列,将finally替换为then即可

避坑提示

不要使用手动布尔锁的实现方式,类似下面的写法在高频调用场景下会出现并发问题:

// 错误实现,请勿使用
let isRunning = false;
async function wrongImpl() {
  if (isRunning) return;
  isRunning = true;
  try {
    await longRunningAsyncFunc();
  } finally {
    isRunning = false;
  }
}

该写法的问题在于:前一个任务执行完毕将isRunning置为false的瞬间,多个积压的等待调用会同时通过条件判断进入执行阶段,仍然会产生并发。

效果验证

用1秒延时模拟长耗时任务做测试:

// 模拟长耗时异步函数
async function longRunningAsyncFunc(seq) {
  console.log('启动任务', seq);
  await new Promise(resolve => setTimeout(resolve, 1000));
  console.log('结束任务', seq);
}

// 短时间内连续触发3次调用
enqueueTask(1);
enqueueTask(2);
enqueueTask(3);

执行输出顺序如下,总耗时3秒,无并发执行情况:

启动任务 1
(等待1秒)
结束任务 1
启动任务 2
(等待1秒)
结束任务 2
启动任务 3
(等待1秒)
结束任务 3

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 08:45:37