javascriptasync-await

Any difference between await Promise.all() and multiple await?


Is there any difference between:

const [result1, result2] = await Promise.all([task1(), task2()]);

and

const t1 = task1();
const t2 = task2();

const result1 = await t1;
const result2 = await t2;

and

const [t1, t2] = [task1(), task2()];
const [result1, result2] = [await t1, await t2];

Solution

  • Note: this answer just covers the timing differences between await in series and Promise.all. Be sure to read @mikep's comprehensive answer that also covers the more important differences in error handling.


    For the purposes of this answer I will be using some example methods:

    Calling res starts the timer. Using Promise.all to wait for a handful of delays will resolve after all the delays have finished, but remember they execute at the same time:

    Example #1

    const data = await Promise.all([res(3000), res(2000), res(1000)])
    //                              ^^^^^^^^^  ^^^^^^^^^  ^^^^^^^^^
    //                               delay 1    delay 2    delay 3
    //
    // ms ------1---------2---------3
    // =============================O delay 1
    // ===================O           delay 2
    // =========O                     delay 3
    //
    // =============================O Promise.all
    

    async function example() {
      const start = Date.now()
      let i = 0
      function res(n) {
        const id = ++i
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve()
            console.log(`res #${id} called after ${n} milliseconds`, Date.now() - start)
          }, n)
        })
      }
    
      const data = await Promise.all([res(3000), res(2000), res(1000)])
      console.log(`Promise.all finished`, Date.now() - start)
    }
    
    example()

    This means that Promise.all will resolve with the data from the inner promises after 3 seconds.

    But, Promise.all has a "fail fast" behavior:

    Example #2

    const data = await Promise.all([res(3000), res(2000), rej(1000)])
    //                              ^^^^^^^^^  ^^^^^^^^^  ^^^^^^^^^
    //                               delay 1    delay 2    delay 3
    //
    // ms ------1---------2---------3
    // =============================O delay 1
    // ===================O           delay 2
    // =========X                     delay 3
    //
    // =========X                     Promise.all
    

    async function example() {
      const start = Date.now()
      let i = 0
      function res(n) {
        const id = ++i
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve()
            console.log(`res #${id} called after ${n} milliseconds`, Date.now() - start)
          }, n)
        })
      }
      
      function rej(n) {
        const id = ++i
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            reject()
            console.log(`rej #${id} called after ${n} milliseconds`, Date.now() - start)
          }, n)
        })
      }
      
      try {
        const data = await Promise.all([res(3000), res(2000), rej(1000)])
      } catch (error) {
        console.log(`Promise.all finished`, Date.now() - start)
      }
    }
    
    example()

    If you use async-await instead, you will have to wait for each promise to resolve sequentially, which may not be as efficient:

    Example #3

    const delay1 = res(3000)
    const delay2 = res(2000)
    const delay3 = rej(1000)
    
    const data1 = await delay1
    const data2 = await delay2
    const data3 = await delay3
    
    // ms ------1---------2---------3
    // =============================O delay 1
    // ===================O           delay 2
    // =========X                     delay 3
    //
    // =============================X await
    

    async function example() {
      const start = Date.now()
      let i = 0
      function res(n) {
        const id = ++i
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            resolve()
            console.log(`res #${id} called after ${n} milliseconds`, Date.now() - start)
          }, n)
        })
      }
      
      function rej(n) {
        const id = ++i
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            reject()
            console.log(`rej #${id} called after ${n} milliseconds`, Date.now() - start)
          }, n)
        })
      }
      
      try {
        const delay1 = res(3000)
        const delay2 = res(2000)
        const delay3 = rej(1000)
    
        const data1 = await delay1
        const data2 = await delay2
        const data3 = await delay3
      } catch (error) {
        console.log(`await finished`, Date.now() - start)
      }
    }
    
    example()