并行调用 async/await 函数

新手上路,请多包涵

据我了解,在 ES7/ES2016 中,在代码中放置多个 await 的工作方式类似于将 .then() 与 Promise 链接起来,这意味着它们将一个接一个地执行而不是并行执行。因此,例如,我们有以下代码:

await someCall();
await anotherCall();

我是否正确理解 anotherCall() 只有在 someCall() 完成时才会被调用?并行调用它们的最优雅方式是什么?

我想在 Node 中使用它,所以也许有异步库的解决方案?

编辑:我对这个问题中提供的解决方案不满意: Slowdown due to non-parallel waiting of promises in async generators ,因为它使用了生成器,我在问一个更一般的用例。

原文由 Victor Marchuk 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 985
2 个回答

您可以在 Promise.all() 上等待:

 await Promise.all([someCall(), anotherCall()]);

存储结果:

 let [someResult, anotherResult] = await Promise.all([someCall(), anotherCall()]);

请注意, Promise.all 快速失败,这意味着一旦提供给它的承诺之一被拒绝,那么整个事情都会被拒绝。

 const happy = (v, ms) => new Promise((resolve) => setTimeout(() => resolve(v), ms))
 const sad = (v, ms) => new Promise((_, reject) => setTimeout(() => reject(v), ms))

 Promise.all([happy('happy', 100), sad('sad', 50)])
 .then(console.log).catch(console.log) // 'sad'

相反,如果您想等待所有的 promise 完成或拒绝,那么您可以使用 Promise.allSettled 。请注意,Internet Explorer 本身并不支持此方法。

 const happy = (v, ms) => new Promise((resolve) => setTimeout(() => resolve(v), ms))
 const sad = (v, ms) => new Promise((_, reject) => setTimeout(() => reject(v), ms))

 Promise.allSettled([happy('happy', 100), sad('sad', 50)])
 .then(console.log) // [{ "status":"fulfilled", "value":"happy" }, { "status":"rejected", "reason":"sad" }]

注意: 如果您使用 Promise.all ,所有在拒绝发生之前成功完成的操作都不会回滚,因此您可能需要注意这种情况。例如

如果您有 5 个动作,4 个快速,1 个慢速和慢速拒绝。那4个

操作可能已经执行,因此您可能需要回滚。在这种情况下,请考虑使用 Promise.allSettled ,同时它将提供哪些操作失败和哪些失败的确切细节。

原文由 madox2 发布,翻译遵循 CC BY-SA 4.0 许可协议

长话短说

使用 Promise.all 进行并行函数调用,发生错误时答案行为不正确。


首先,一次执行 所有 异步调用并获取所有 Promise 对象。其次,在 Promise 对象上使用 await 。这样,当您等待第一个 Promise 解决时,其他异步调用仍在进行中。总的来说,您只会等待最慢的异步调用。例如:

 // Begin first call and store promise without waiting
const someResult = someCall();

// Begin second call and store promise without waiting
const anotherResult = anotherCall();

// Now we await for both results, whose async processes have already been started
const finalResult = [await someResult, await anotherResult];

// At this point all calls have been resolved
// Now when accessing someResult| anotherResult,
// you will have a value instead of a promise

JSbin 示例: http ://jsbin.com/xerifanima/edit?js,console

警告: 无论 await 调用在同一行还是不同行都没有关系,只要第一个 await 调用发生 所有异步调用之后。请参阅 JohnnyHK 的评论。


更新: 根据 @bergi 的回答,此答案在错误处理方面有不同的时间安排,它 不会 在错误发生时抛出错误,而是在执行所有承诺之后。我将结果与@jonny 的提示进行比较: [result1, result2] = Promise.all([async1(), async2()]) ,检查以下代码片段

 const correctAsync500ms = () => {
  return new Promise(resolve => {
    setTimeout(resolve, 500, 'correct500msResult');
  });
};

const correctAsync100ms = () => {
  return new Promise(resolve => {
    setTimeout(resolve, 100, 'correct100msResult');
  });
};

const rejectAsync100ms = () => {
  return new Promise((resolve, reject) => {
    setTimeout(reject, 100, 'reject100msError');
  });
};

const asyncInArray = async (fun1, fun2) => {
  const label = 'test async functions in array';
  try {
    console.time(label);
    const p1 = fun1();
    const p2 = fun2();
    const result = [await p1, await p2];
    console.timeEnd(label);
  } catch (e) {
    console.error('error is', e);
    console.timeEnd(label);
  }
};

const asyncInPromiseAll = async (fun1, fun2) => {
  const label = 'test async functions with Promise.all';
  try {
    console.time(label);
    let [value1, value2] = await Promise.all([fun1(), fun2()]);
    console.timeEnd(label);
  } catch (e) {
    console.error('error is', e);
    console.timeEnd(label);
  }
};

(async () => {
  console.group('async functions without error');
  console.log('async functions without error: start')
  await asyncInArray(correctAsync500ms, correctAsync100ms);
  await asyncInPromiseAll(correctAsync500ms, correctAsync100ms);
  console.groupEnd();

  console.group('async functions with error');
  console.log('async functions with error: start')
  await asyncInArray(correctAsync500ms, rejectAsync100ms);
  await asyncInPromiseAll(correctAsync500ms, rejectAsync100ms);
  console.groupEnd();
})();

原文由 Haven 发布,翻译遵循 CC BY-SA 4.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题