如何顺序执行多个异步操作?

在JavaScript中,我们常常需要让多个异步操作按顺序依次执行,即下一个操作必须等待上一个操作完成才能开始。这在处理有依赖关系的任务(如分步上传、串联API调用)时至关重要。

使用async/await的for循环

最清晰和灵活的方式是在async函数内部使用for循环来遍历你的任务集合,并在循环体内使用await等待每个任务完成。

async function sequentialTasks(tasks) {
  const results = [];
  for (const task of tasks) {
    const result = await task(); // 等待当前任务完成
    results.push(result);
  }
  return results;
}

// 示例任务
const task1 = () => new Promise(res => setTimeout(() => { console.log('任务1'); res(1); }, 1000));
const task2 = () => new Promise(res => setTimeout(() => { console.log('任务2'); res(2); }, 500));
sequentialTasks([task1, task2]).then(results => console.log('所有结果:', results));

输出会严格按照“任务1”、“任务2”、“所有结果: [1, 2]”的顺序出现。for循环确保了迭代的同步性,而await确保了异步操作的顺序执行。

使用reduce串联Promise

另一种函数式的方法是使用reduce。它通过将前一个Promise的结果传递给下一个Promise来构建一个串联的Promise链。

function sequentialPromise(tasks) {
  return tasks.reduce((promiseChain, currentTask) => {
    return promiseChain.then(chainResults =>
      currentTask().then(currentResult => [...chainResults, currentResult])
    );
  }, Promise.resolve([]));
}

sequentialPromise([task1, task2]).then(results => console.log('串联结果:', results));

这里的reduce从一个已解决的Promise(Promise.resolve([]))开始,每次迭代都将一个新的.then()链接到链条上。这能实现同样的顺序执行,但代码逻辑比async/await版本稍显复杂。

手动构建Promise链

对于已知且固定的几个异步任务,最直接的方法就是手动await每一个。

async function fixedSequence() {
  const res1 = await fetch('/api/first');
  const data1 = await res1.json();
  const res2 = await fetch(`/api/second/${data1.id}`);
  const data2 = await res2.json();
  return data2;
}

这种方法简单明了,适用于步骤明确、数量不多的场景。当步骤动态或数量可变时,前两种方法更合适。

选择方案与注意事项

使用async/await配合循环是当前最推荐的做法,它代码直观,易于添加错误处理(用try...catch包裹循环体或单个await),且性能良好。

async function sequentialWithErrorHandling(tasks) {
  const results = [];
  for (const task of tasks) {
    try {
      results.push(await task());
    } catch (error) {
      console.error('某个任务失败:', error);
      // 决定是中断还是继续
      break; // 或 continue
    }
  }
  return results;
}

reduce方案更函数式,但错误处理需要融入链中。无论哪种方式,其本质都是让每个异步操作在前一个操作的Promise解决后才开始,从而保证了严格的执行顺序。

© 版权声明
THE END
喜欢就支持一下吧
点赞5 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容