-2

In the following snippet the resolution time is different when using promise#all and sequentially await for each promise. I want to deeply understand what is going on, since it seems to be a caveat when dealing with performance.

*Edit: added another case with no function invocation, probably I am missing something with how await works

// Code goes here

window.onload = function() {
  
    let p = new Promise((res, rej) => {
        setTimeout(() => {
            res('p OK')
        }, 5000);
    })
   
    let p2 = new Promise((res, rej) => {
        setTimeout(() => {
            res('p2 OK')
        }, 5000);
    })
    
    let p3 = new Promise((res, rej) => {
        setTimeout(() => {
            res('p3 OK')
        }, 5000);
    })
  
    async function pp() {
        return new Promise((res, rej) => {
            setTimeout(() => {
                res('pp OK');
            }, 5000);
        });
    }
  
    async function a() {
        let out = await Promise.all([p, pp()]);
        return `#a ${out}`;
    }
  
    async function b() {
       let out1 = await p;
       let out2 = await pp();
       return `#b ${out1} ${out2}`;
    }
    
    async function c() {
        let out1 = await p;
        let out2 = await p2;
        let out3 = await p3;
        return `#c ${out1} ${out2} ${out3}`;
    }
  
    let out1 = document.getElementById("out1");
    let out2 = document.getElementById("out2");
    let out32 = document.getElementById("out2");
    const date = new Date().getSeconds();
    a().then(x => console.log(x)).then(() => out1.innerHTML += `finished after ${new Date().getSeconds() - date}s`);
    b().then(x => console.log(x)).then(() => out2.innerHTML += `finished after ${new Date().getSeconds() - date}s`);
    c().then(x => console.log(x)).then(() => out3.innerHTML += `finished after ${new Date().getSeconds() - date}s`);

}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<!DOCTYPE html>
<html>

<head>
    <link rel="stylesheet" href="style.css">
    <script src="script.js"></script>
</head>

<body>
    <p id="out1">
        Promise#all  
    </p>
    <p id="out2">
        Sequential awaits
    </p>
    <p id="out3">
        Sequential awaits without executing fnc
    </p>
</body>

</html>
jeremyTob
  • 131
  • 10
  • relevant: https://stackoverflow.com/questions/46889290/waiting-for-more-than-one-concurrent-await-operation – Bergi Jan 13 '19 at 12:23

2 Answers2

2

Some points that may clarify things:

  • The constructor callback function that is passed to new Promise is executed immediately, meaning the setTimeout delay kicks in at that moment, whether you are going to await it or not.

  • By consequence, the timeouts created by p, p2 and p3 are all initiated as soon as those variables are initialised. As they all will time out after 5 seconds, the corresponding promises (p, p2 and p3) will get resolved at about the same time. Again, this is independent on whether you use await, then, Promise.all or just forget about those promises and don't do anything with them.

  • By contrast, the function pp is just a function. It is not a promise. Only when you actually call that function, you create a promise with the corresponding timeout that initiates.

  • await makes the async function return immediately, returning a promise. This means that the rest of the function's code is postponed, but not the rest of your other Javascript code: the function returns and execution continues after that function call. When the call stack is empty, the different event queues will be processed. So it is not blocking. Once the promise that was passed to await resolves, something magically happens: the async's function execution context is restored and the execution resumes until the next await. If there is no more await to execute, and the function gets to its end, the promise it returned (when it encountered the first await) is resolved.

  • Promise.all does not influence when the individual promises resolve. It creates a new promise that resolves when all the given promises have resolved. As explained above, the promises in your case (p, p2) resolve at about the same moment, so Promise.all will also resolve after about 5 seconds.

  • If you do await pp() you only create that promise there and then, not earlier. And because you have an await p before it, it will take 5 seconds before pp() is executed. So the corresponding timeout does not start until this second await is executed. That is why await p; await pp() takes twice 5 seconds to resolve.

  • The case is different when you do await p; await p2. There both promises already were created, and their timeouts already started. So when after 5 seconds the first await is over, JS will get to the second await and will find that p2 is also resolved. There will not be any extra waiting period (except for the asynchronous management where await will always put an event in the microtask queue)

I think with these elements you can correctly picture how the code will execute, and how the output you get is as expected.

trincot
  • 317,000
  • 35
  • 244
  • 286
0

In the first function, it executes pp straight away to start the promise timeout. In the second function, it waits until p has been completed before it executes pp to start the promise timeout.

The await keyword "blocks*" execution until the promise resolves before continuing on to the next line.

RE: the update

In your third example, all the promises are started at the time. The await keyword waits until the promise is resolved. Because all your promises start at the same time, they will all resolve within a very small delta. By the time you hit await p2, it's likely already resolved. Same for p3.

In your second example, pp doesn't start until p completes.

[*] It doesn't really block execution, but that's the impact it has on your code.

Evan Trimboli
  • 29,900
  • 6
  • 45
  • 66
  • can you please check the updated snippet? I have included a function c awaiting 3 promises which returns after 5 seconds as well. It is not blocked in any way. Thank you – jeremyTob Jan 13 '19 at 11:37
  • can you please elaborate on why the promises in the last example are started at the same time? – jeremyTob Jan 27 '19 at 16:45
  • Because they are all created at the same time. The promise under `pp` is only created when the function is called. – Evan Trimboli Jan 27 '19 at 20:26