How is async/await working in serial and parallel?
Asked Answered
H

4

10

I have two async functions. Both of them are waiting for two 3 seconds function calls. But the second one is faster than the first. I think the faster one is running in parallel and other in serial. Is my assumption correct? If yes, why is this happening as both the functions look logically same?

function sleep() {
  return new Promise(resolve => {
    setTimeout(resolve, 3000);
  });
}

async function serial() {
  await sleep();
  await sleep();
}

async function parallel() {
  var a = sleep();
  var b = sleep();
  await a;
  await b;
}

serial().then(() => {
  console.log("6 seconds over");
});

parallel().then(() => {
  console.log("3 seconds over");
});
Hayrick answered 3/8, 2017 at 8:52 Comment(10)
Why do you think setTimeout is async? There's nothing async in your code except function declarations.Cleora
@Cleora Errm, setTimeout is by it's very nature asynchronous...and pretty much everything else in OP's code is asynchronous as well...Courtyard
@Frxstrem except setTimeout is not asynchronous at all. It's a synchronous function that tells the event loop to execute a function at a later time. It's a mechanism, there's absolutely NOTHING asynchronous in this code. Which two moving parts are moving at different speeds, in different execution contexts? Zero. Hence, this code isn't async at all.Cleora
@Cleora The main context, the callback to setTimeout, both serial and parallel, and the callbacks to both calls to .then, all run in different executing contexts.Courtyard
@Frxstrem this all works in the same thread, the execution context is not different, there are no two moving parts, the function in question setTimeout is synchronous. Emulating the mechanism of async behaviour does not mean something is async. But hey, if you prefer to believe it is - I'm just a mere mortal telling you a story you don't like.Cleora
@Mjh, This is what exactly "async/asynchronous" means in javascript. They need not be running in multiple threads. Though currently javascript allows using background threads.Hayrick
@Hayrick whatever rocks your world, if you can't make out the difference between mechanism that notifies the thread with event loop and actual, asynchronous operations then ok, excellent, let's make that async in javascript world :)Cleora
@Cleora We are using the term "async/asynchronous" because in javascript that's the official word for it. "asynchronous programming" is the term used by ECMAScript 2017 language specification. tc39.github.io/ecma262/2017Hayrick
@Hayrick why would you explain what async is when, obviously, I'm more than familiar with the term, spec and obviously how it works behind the scenes. There's a difference between a mechanism of async (the way event loop gets its notifications) and actual async code. You're currently just using the mechanism via synchronous code. Sure, you can emulate the behavior. But, your code is not asynchronous. Now, I'll stop because no one is going to budge so it's best we leave it be as it is.Cleora
@Cleora It's been nice talking with you..Hayrick
C
10

Frxstream already has an excellent answer. To build on that, and provide some perspective:

The first thing to recognize is that Promises are created "hot" - that is, by the time you have a promise object, it is already "in progress".

The second important concept is that await is like an "asynchronous wait" - that is, it pauses the execution of the function until that promise completes.

So, the serial function calls sleep, gets a promise back, and then (asynchronously) waits for that promise to complete. After that promise completes 3 seconds later, serial again calls sleep, gets a promise back, and then (asynchronously) waits for that promise to complete. After that promise completes 3 seconds later, serial completes.

The parallel function calls sleep and stores its promise in a, and then calls sleep and stores its promise in b, and then (asynchronously) waits for a to complete. After a completes 3 seconds later, parallel (asynchronously) waits for b to complete. After b completes practically immediately, parallel completes.

Chipman answered 3/8, 2017 at 14:16 Comment(0)
C
10

It's clearer if you write your serial function like this:

async function serial() {
  var a = sleep(); //
  await a;         // await sleep();

  var b = sleep(); //
  await b;         // await sleep();
}

async function parallel() {
  var a = sleep();
  var b = sleep();
  await a;
  await b;
}

Here you can clearly see that in the serial function, the second sleep() is only called after the first sleep() has completed, while in parallel it's called immediately, before the first has completed, and then it waits for both to complete. So while they may look functionally identical, they are subtly different.

Courtyard answered 3/8, 2017 at 9:2 Comment(3)
Mind if you took a look from the perspective of this question: https://mcmap.net/q/36615/-waiting-for-more-than-one-concurrent-await-operation/919480 ?Dollfuss
@Dollfuss - I think Frxstrem was just explaining the difference you asked about in your question, not suggesting that you should necessarily do it that way. serial above is fine if you want the operations running in series (one after the other). parallel above is indeed better implemented as await Promise.all([sleep(), sleep()]); but again, I don't think that's the point of the answer above. The point of the answer above (I think) is to explain the timing difference in the question.Isaiasisak
Also, var a = sleep(); var b = sleep(); await a; await b; works just the same.Legitimate
C
10

Frxstream already has an excellent answer. To build on that, and provide some perspective:

The first thing to recognize is that Promises are created "hot" - that is, by the time you have a promise object, it is already "in progress".

The second important concept is that await is like an "asynchronous wait" - that is, it pauses the execution of the function until that promise completes.

So, the serial function calls sleep, gets a promise back, and then (asynchronously) waits for that promise to complete. After that promise completes 3 seconds later, serial again calls sleep, gets a promise back, and then (asynchronously) waits for that promise to complete. After that promise completes 3 seconds later, serial completes.

The parallel function calls sleep and stores its promise in a, and then calls sleep and stores its promise in b, and then (asynchronously) waits for a to complete. After a completes 3 seconds later, parallel (asynchronously) waits for b to complete. After b completes practically immediately, parallel completes.

Chipman answered 3/8, 2017 at 14:16 Comment(0)
T
3

Because thesleep()function is a synchronous function, it just return a asynchronous promise, eg:

function sleep (time) {
    return new Promise((resolve) => setTimeout(resolve, time));
}

In parallel(), the two sleep() synchronously init two promise,they wait to be resolved meanwhile, it's going to be about 3s.

However, in serial(), the two await sleep() means that the second sleep() promise must wait for the first sleep() to be resolved, so it's going to be about 6s.

Thayne answered 3/8, 2017 at 9:24 Comment(0)
T
1

Serial Run: Run the functions one after another

// 🚫 It’s slow! and use only for serial(one after another) run
async function doThings() {
    const thing1 = await asyncThing1(); // waits until resolved/rejected
    console.log(thing1);

    const thing2 = await asyncThing2(); // starts only after asyncThing1() has finished.
    console.log(thing2);
}

doThings();

Parallel Run: Run the functions parallel

// ✅ async code is run in parallel!
async function doThings() {
    const p1 = asyncThing1(); // runs parallel
    const p2 = asyncThing2(); // runs parallel


    // Method 1: Prefer => Promise.all()
    const [resultThing1, resultThing2] = await Promise.all([p1, p2]); // waits for all 
    // the promises get resolved or fails fast (If one of the promises supplied to it 
    // rejects, then the entire thing rejects).

    // Method 2: Not-Preferred
    const resultThing1 = await p1;
    const resultThing2 = await p2;

    console.log(resultThing1); // reaches here only after both the p1 & p2 have completed.
    console.log(resultThing2);
}

doThings();
Triviality answered 20/12, 2021 at 15:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.