并行调用异步/等待函数

据我所知,在ES7 / ES2016中,将多个's放入代码中的工作方式类似于用承诺链接,这意味着它们将一个接一个地执行,而不是并行执行。因此,例如,我们有以下代码:await.then()

await someCall();
await anotherCall();

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

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

编辑:我对这个问题中提供的解决方案不满意:由于异步生成器中非并行等待承诺而减速,因为它使用生成器,我正在询问一个更通用的用例。


答案 1

您可以等待: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.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" }]

注意:如果您使用的操作在拒绝发生之前设法完成,则不会回滚,因此您可能需要处理这种情况。例如,如果您有 5 个操作,4 个快速,1 个慢速和慢速拒绝。这 4 个操作可能已执行,因此您可能需要回滚。在这种情况下,请考虑使用,而它将提供确切的详细信息,哪些操作失败,哪些操作未失败。Promise.allPromise.allSettled


答案 2

TL;DR

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


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

// 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

警告:无论调用是在同一行还是在不同的线路上,只要在所有异步调用之后发生第一个调用即可。请参阅JohnnyHK的评论。awaitawait


更新:根据@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();
})();