2

In the following code, the 'Hey, timeout executed!' is only printed after the loop in the longLoop function is finished.

setTimeout(() => console.log('Hey, timeout executed!'), 100);

let longLoop = async () => {
    for (i = 0; i < 1000000000; i++) {
    }
}

let exec = async () => {
    await longLoop();

    console.log('Loop finished')

}

exec()

However, when I change the longLoop to have an await inside it, the 'Hey, timeout executed!' will print as soon as the await in the loop is reached, like in the following:

setTimeout(() => console.log('Hey, timeout executed!'), 100);

let longLoop = async () => {
    for (i = 0; i < 1000000000; i++) {
        await new Promise(r => setTimeout(r, 100));
    }
}

let exec = async () => {
    await longLoop();

    console.log('Loop finished')

}

exec()

Why does in the first example the event loop seems to be blocked even if I call the longLoop from the exec using an await? And why it doesn't get blocked in the second example just by using an await inside the loop?

I'm just starting in JavaScript, so I thought that just by a function being async and being called with an await inside another async function, it would be executed in a way that the event loop would not be blocked. Due to that, I'm having kinda a hard time to understand the flow of execution.

  • 3
    "*I thought that just by a function being async, it would be executed in a way that the event loop would not be blocked*" - no. Marking something as `async` only allows the usage of the `await` keyword to wait for promises (asynchronous results). The code between the `await`s is still executed normally on the event loop, and calling long-running blocking functions is still blocking. See also [here](https://stackoverflow.com/questions/53876344/proper-way-to-write-nonbloking-function-in-node-js) – Bergi May 13 '22 at 12:36
  • 2
    "*a function being async and being called with an await inside another async function*" - whether the result of a function call is `await`ed or not has no effect on how the function is called. [An `async function` simply returns a promise](https://stackoverflow.com/q/63731677/1048572), that's all. – Bergi May 13 '22 at 12:37
  • I think you're confusing `async` with coroutines. An `async` function is just syntactical sugar for a function returning a `Promise`, there's nothing magic about it. JavaScript is single threaded so if you do something CPU-intensive, like the busy waiting in your example, the processor in the main thread will be busy doing that – Christian Vincenzo Traina May 13 '22 at 12:48
  • @CristianTraìna Coroutines wouldn't be processed in parallel on multiple threads either? – Bergi May 13 '22 at 12:52
  • @Bergi that wasn't the point of the comment, but yes if you configure them to do that (at least in Kotlin). If we consider the `await` like some kind of `join`, then the output will be what OP expected – Christian Vincenzo Traina May 13 '22 at 12:57
  • @CristianTraìna Ah, Kotlin, I thought you were talking about coroutines in JS (where they are typically implemented with generator functions). The term is unfortunately used for many things… – Bergi May 13 '22 at 13:00
  • @Bergi to be honest I've never heard talking about coroutines in JS, I heard this term only for Kotlin and Go :) – Christian Vincenzo Traina May 13 '22 at 15:42

1 Answers1

1

This is a case of microtasks vs (macro)tasks. Promises and async/await enter the microtask queue. setTimeout callbacks enter the (macro)task queue. microtasks get run before (macro)tasks (when the stack is empty to be precise), that's why you see the async/await functions get executed before the setTimeout callback

See also: Difference between microtask and macrotask within an event loop context https://stackoverflow.com/a/71195496/4651083

Dimitris Karagiannis
  • 8,942
  • 8
  • 38
  • 63
  • 1
    No, that distinction is not relevant for the OP's code. It's simply that `longLoop()` is a synchronous, blocking loop; it prevents both micro- and macrotasks from running. – Bergi May 13 '22 at 12:51
  • You can set the loop to do 1 iteration and it will behave the same way – Dimitris Karagiannis May 13 '22 at 12:55
  • You can also change the code to do no iterations at all, and it'll behave different again. But the question is about the code from the OP, not something else. – Bergi May 13 '22 at 12:57
  • 1
    This only proves that the task queues have to do with what the OP is observing – Dimitris Karagiannis May 13 '22 at 12:58
  • 1
    Sure it's about the queuing, but it wouldn't be different if there was only a single task queue. The difference between macro tasks and micro tasks is not the answer to the OP's question. – Bergi May 13 '22 at 18:30