并行调用异步/等待功能

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

await someCall();
await anotherCall();

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

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

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

MandyStafan2020/03/17 18:04:07

我投赞成票:

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

请注意,当您调用函数的那一刻,可能会导致意外的结果:

// Supposing anotherCall() will trigger a request to create a new User

if (callFirst) {
  await someCall();
} else {
  await Promise.all([someCall(), anotherCall()]); // --> create new User here
}

但是跟随总是触发创建新用户的请求

// Supposing anotherCall() will trigger a request to create a new User

const someResult = someCall();
const anotherResult = anotherCall(); // ->> This always creates new User

if (callFirst) {
  await someCall();
} else {
  const finalResult = [await someResult, await anotherResult]
}
GO逆天2020/03/17 18:04:06
    // A generic test function that can be configured 
    // with an arbitrary delay and to either resolve or reject
    const test = (delay, resolveSuccessfully) => new Promise((resolve, reject) => setTimeout(() => {
        console.log(`Done ${ delay }`);
        resolveSuccessfully ? resolve(`Resolved ${ delay }`) : reject(`Reject ${ delay }`)
    }, delay));

    // Our async handler function
    const handler = async () => {
        // Promise 1 runs first, but resolves last
        const p1 = test(10000, true);
        // Promise 2 run second, and also resolves
        const p2 = test(5000, true);
        // Promise 3 runs last, but completes first (with a rejection) 
        // Note the catch to trap the error immediately
        const p3 = test(1000, false).catch(e => console.log(e));
        // Await all in parallel
        const r = await Promise.all([p1, p2, p3]);
        // Display the results
        console.log(r);
    };

    // Run the handler
    handler();
    /*
    Done 1000
    Reject 1000
    Done 5000
    Done 10000
    */

设置p1,p2和p3并不是严格并行运行它们,但是它们不会阻止任何执行,因此您可以通过catch捕获上下文错误。

JimNear2020/03/17 18:04:06

这可以通过Promise.allSettled()来完成,这类似于Promise.all()但没有故障快速行为。

async function failure() {
    throw "Failure!";
}

async function success() {
    return "Success!";
}

const [failureResult, successResult] = await Promise.allSettled([failure(), success()]);

console.log(failureResult); // {status: "rejected", reason: "Failure!"}
console.log(successResult); // {status: "fulfilled", value: "Success!"}

注意:这是一项浏览器支持有限的最新功能,因此我强烈建议为此功能包括一个polyfill。

十三SamJim2020/03/17 18:04:06

我创建了要点,测试了解决承诺和结果的几种不同方法。查看有效的选项可能会有所帮助。

理查德神无飞云2020/03/17 18:04:06

您可以等待Promise.all()

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

要存储结果:

let [someResult, anotherResult] = await Promise.all([someCall(), anotherCall()]);
乐米亚2020/03/17 18:04:06

没有Promise.all()的另一种方法是并行执行:

首先,我们有2个打印数字的功能:

function printNumber1() {
   return new Promise((resolve,reject) => {
      setTimeout(() => {
      console.log("Number1 is done");
      resolve(10);
      },1000);
   });
}

function printNumber2() {
   return new Promise((resolve,reject) => {
      setTimeout(() => {
      console.log("Number2 is done");
      resolve(20);
      },500);
   });
}

这是顺序的:

async function oneByOne() {
   const number1 = await printNumber1();
   const number2 = await printNumber2();
} 
//Output: Number1 is done, Number2 is done

这是并行的:

async function inParallel() {
   const promise1 = printNumber1();
   const promise2 = printNumber2();
   const number1 = await promise1;
   const number2 = await promise2;
}
//Output: Number2 is done, Number1 is done
蛋蛋西里2020/03/17 18:04:06

更新:

原始答案使得很难(在某些情况下是不可能)正确处理承诺拒绝。正确的解决方案是使用Promise.all

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;