提问者:小点点

并行调用Async/Await函数


据我所知,在ES7/ES2016中,将多个与承诺链接在一起,这意味着它们将一个接一个地执行,而不是并行地执行。例如,我们有这样的代码:

await someCall();
await anotherCall();

只有当完成时才会调用,我是否正确理解了这一点?并行调用它们最优雅的方式是什么?

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

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


共3个答案

匿名用户

您可以等待:

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

要存储结果,请执行以下操作:

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

请注意,会快速失败,这意味着只要提供给它的承诺之一被拒绝,那么整个承诺就会被拒绝。

null

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'

匿名用户

TLDR

对于并行函数调用使用,当错误发生时,应答的行为将不正确。

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

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

注意:只要第一个调用发生在所有异步调用之后,那么调用是在同一行还是在不同行都没有关系。参见JohnnyHK的评论。

更新:根据@bergi's的回答,这个回答在错误处理中有一个不同的时间,它不会在错误发生时抛出错误,而是在所有承诺被执行后抛出错误。我将结果与@jonny's tip进行比较:,检查以下代码片段

null

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();
})();

匿名用户

更新:

最初的答案使得正确处理承诺拒绝变得困难(在某些情况下甚至是不可能的)。正确的解决方案是使用:

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

原答:

只需确保在等待任一个函数之前调用两个函数:

// Call both functions
const somePromise = someCall();
const anotherPromise = anotherCall();

// Await both promises    
const someResult = await somePromise;
const anotherResult = await anotherPromise;