19

I have already gone through the thread Any difference between await Promise.all() and multiple await?, so I am clear about Promise.all and multiple awaits.

Still, I am not very clear about the below 2 scenarios.

In Case 1 why does it execute sequentially (takes 10s) whereas in Case 2 it executes in parallel (takes 4s)?

Case 1:

function promiseWait(time) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(true);
    }, time);
  });
}

async function test1() {
  var t0 = performance.now()

  var a = await promiseWait(1000)
  var b = await promiseWait(2000)
  var c = await promiseWait(3000)
  var d = await promiseWait(4000)

  var t1 = performance.now()
  console.log("Call to doSomething took " + (t1 - t0) + " milliseconds."); //takes 10secs
  
}
test1()

Case 2:

function promiseWait(time) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(true);
    }, time);
  });
}

async function test2() {
  var t0 = performance.now()

  const p1 = promiseWait(1000);
  const p2 = promiseWait(2000);
  const p3 = promiseWait(3000);
  const p4 = promiseWait(4000);

  const a = await p1;
  const b = await p2;
  const c = await p3;
  const d = await p4;

  var t1 = performance.now()
  console.log("Call to doSomething took " + (t1 - t0) + " milliseconds.")
}

test2()
Boann
  • 48,794
  • 16
  • 117
  • 146
Deepak Kumar Padhy
  • 4,128
  • 6
  • 43
  • 79
  • 6
    because in 1) at each await waits for the promise to settle before the next statement is executed - in 2) you begin the promises without waiting for the previous to settle - so they run in parallel – Jaromanda X Oct 13 '20 at 05:12
  • `await` will *pause the execution of the function* until the value you give it resolves. – VLAZ Oct 13 '20 at 05:14
  • @JaromandaX, Then my questions is, why does not it waits in Case2 and executes all in 4s? – Deepak Kumar Padhy Oct 13 '20 at 05:14
  • 1
    because it actually DOES ... between those awaits ... put some console.logs in there if you don't believe me ... they'll output 1 second apart as each promise settles – Jaromanda X Oct 13 '20 at 05:15
  • @DeepakKumarPadhy in case 2 all 4 promises are started together. You can imagine the timer runs parallel in the background for all 4 promises. So everything is done at around 4 sec – bill.gates Oct 13 '20 at 05:15
  • @JaromandaX, thanks for your edited explanation, it makes sense now. Out of the context, Promise.allSettled executes all the promises in parallel right? – Deepak Kumar Padhy Oct 13 '20 at 05:19
  • 1
    .allSettled is like .all - but never rejects, is always resolved - the results show individual resolve/reject status ... whereas with .all, one rejection rejects .all – Jaromanda X Oct 13 '20 at 05:23
  • 2
    Promises are **only** completed “in parallel” when the *underlying backing*, such as an XHR request, runs “in parallel” (in practice, there is a limit to such, such as 6 requests/domain). Concurrency != Parallelism. – user2864740 Oct 13 '20 at 05:23
  • 1
    If you know why/how `Promise.all` works, then you should know how this works. It's quite similar. If you do `await Promise.all([foo(), bar(), baz()])`, then you are invoking `foo`, `bar`, `baz` *first* (i.e. kicking off all the promises) and pass their return values to `Promise.all`. It's the same as `var p1 = foo(); var p2 = bar(); var p3 = baz(); await Promise.all([p1,p2,p3])`, which in turn is "similar" (not quite the same) as `var p1 = foo(); var p2 = bar(); var p3 = baz(); await p1; await p2; await p3;`. – Felix Kling Oct 13 '20 at 11:23
  • @JaromandaX don't answer in comments, write an answer – Matsemann Oct 19 '20 at 08:03
  • @Matsemann - I made the comment because sometimes that's enough to trigger the OP into realising what to do - 2 minutes later an answer was posted, so no point in adding another answer – Jaromanda X Oct 19 '20 at 08:34

3 Answers3

12

In the first case because of the await before each call to the promiseWait, to even start executing the next call to the promiseWait it needs to wait until the first call to the promiseWait is finished completely. So you see sequential execution.

In the second case you have already invoked all the promiseWait functions before you start awaiting them. So the promiseWait has already started executing, then you are awaiting the results one after another.

Implementation wise in the first scenario, the next call to the setTimeout has to wait until the first setTimeout expires. So the second, third and the fourth timers need to wait until the first timer expires and resolves the promise in order to be scheduled.

In the seconds case you schedule the setTimeout calls one after the other, so the timers are all already queued. Then you are just awaiting for the timers to expire and resolve your promise one by one.

Fullstack Guy
  • 16,368
  • 3
  • 29
  • 44
4

With some more deep dive ... (Inspired by Philip Roberts).

Case 1: SetTimeout triggers one by one

function promiseWait(time) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(new Date().toISOString().split('.')[0].replace('T',' '));
    }, time);
  });
}

async function test1() {
  let timeStamp = new Date();
  var t0 = new Date();
  console.log("t0",t0)

  var a = await promiseWait(1000)
  console.log("a",a)
  var b = await promiseWait(2000)
  console.log("b",b)
  var c = await promiseWait(3000)
  console.log("c",c)
  var d = await promiseWait(4000)
  console.log("d",d)
  
  var t1 = new Date();
  console.log("t1",t1)
  console.log("Call to doSomething took " + (t1 - t0) + " milliseconds."); //takes 10secs

}
test1()

Console: You can notice that sum of each timeout is resulted ~10 Secs.

enter image description here

Case 2: SetTimeout triggers at same time.

function promiseWait(time) {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve(new Date().toISOString().split('.')[0].replace('T',' '));
      }, time);
    });
  }
  
  async function test1() {
    let timeStamp = new Date();
    var t0 = new Date();
    console.log("t0",t0)
  
    const p1 = promiseWait(1000);
    const p2 = promiseWait(2000);
    const p3 = promiseWait(3000);
    const p4 = promiseWait(4000);
    
    console.log("p1",p1);
    console.log("p2",p2);
    console.log("p3",p3);
    console.log("p4",p4);

    const a = await p1;
    console.log("a",a);
    const b = await p2;
    console.log("b",b);
    const c = await p3;
    console.log("c",c);
    const d = await p4;
    console.log("d",d);

    var t1 = new Date();
    console.log("t1",t1)
    console.log("Call to doSomething took " + (t1 - t0) + " milliseconds."); //takes 10secs
  
  }
  test1()

Console: Here we can notice that timer started together so it resulted with ~ 4 Secs. (Last timer is set at 4 sec).

enter image description here

iravinandan
  • 659
  • 6
  • 16
3

I this case all 4 promises started at the same time. Therefore its setTimeout timer runs parallel in the background.

The timers have an difference with 1 second so if you put an console log between the awaits you should see around every second a new console log.

function promiseWait(time) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(true);
    }, time);
  });
}

async function test2() {
  var t0 = performance.now()

  console.log("putting promises in que");
  const p1 = promiseWait(1000);
  const p2 = promiseWait(2000);
  const p3 = promiseWait(3000);
  const p4 = promiseWait(4000);
  console.log("promises where added to the que");

  const a = await p1;
    console.log("1")
  const b = await p2;
    console.log("2")
  const c = await p3;
    console.log("3")
  const d = await p4;
    console.log("4")
    
  var t1 = performance.now()
  console.log("Call to doSomething took " + (t1 - t0) + " milliseconds.")
}

test2()

Visualized it looks something like this:

setTimeout(() => {
   let divs = document.querySelectorAll("div").forEach(div => {
      div.style.setProperty("width", "100%")
   })
},500)
div {
   height: 30px;
   background: red;
   margin: 10px;
   width: 0px;
   transition: width 4s;
   color: white
}
<div style="max-width: 100px";>
Promise 1
</div>
<div style="max-width: 200px";>
Promise 2
</div>
<div style="max-width: 300px";>
Promise 3
</div>
<div style="max-width: 400px";>
Promise 4
</div>

Case 1 visualized:

let divs = document.querySelectorAll("div")

let delay = (time) => new Promise(res => setTimeout(res, time));

(async() => {
for(let i = 0; i < divs.length; i++) {
   await delay(i + 1000);
   divs[i].style.setProperty("width", "100%");
}
})()
div {
   height: 30px;
   background: red;
   margin: 10px;
   width: 0px;
   transition: width 4s;
   color: white
}
<div style="max-width: 100px";>
Promise 1
</div>
<div style="max-width: 200px";>
Promise 2
</div>
<div style="max-width: 300px";>
Promise 3
</div>
<div style="max-width: 400px";>
Promise 4
</div>
bill.gates
  • 14,145
  • 3
  • 19
  • 47