1

For the following code, I expect each promise to be started and run. I do not expect the promises to be awaited until the end of the code, and yet for some reason, each operation is run synchronously. Why is this? Am I simply doing something wrong?

#!/usr/bin/env node

const longRunningOperation = async (index) =>{
    console.log("Starting Operation " + index.toString())
    console.time(index)
    const cap = 5_000_000_000;
    let sum = 0
    for(let i = 0; i < cap; i++) sum+=i
    console.timeEnd(index)
}

const op1 = longRunningOperation(1);
const op2 = longRunningOperation(2);
const op3 = longRunningOperation(3);
const op4 = longRunningOperation(4);

console.log("Hi! I run before anything else. Right?")

await Promise.all([op1, op2, op3, op4])

Actual Result:

Starting Operation 1
1: 4.336s
Starting Operation 2
2: 4.740s
Starting Operation 3
3: 11.830s
Starting Operation 4
4: 11.675s
Hi! I run before anything else. Right?

Expected Result (times varying of course):

Hi! I run before anything else. Right?
Starting Operation 1
Starting Operation 2
Starting Operation 3
Starting Operation 4
1: 4.336s
2: 4.740s
3: 11.830s
4: 11.675s
Mike
  • 23,542
  • 14
  • 76
  • 87
SpicyMike
  • 67
  • 1
  • 5
  • 2
    Asynchronous doesn't mean that the code is multithreaded. It still runs on the same thread especially when you don't have anything async in the async function. Moreover, promises don't "start" when you await them. Promises are a marker that an operation is already running. It's a notification mechanism for the result of that operation. For some more information, see [Please point out the misfacts in my learning regarding Asynchronous Javascript](https://stackoverflow.com/q/65833787) – VLAZ Aug 01 '21 at 06:23

1 Answers1

1

What @VLAZ was alluding to is on the right path. A promise spawns a Task object inside the event loop. This is a synchronous operation. By the time this promise is created, the event stack is empty, thus the task is given run time.

It is important to note, that despite being asynchronous, that does not mean the function is performed on a separate thread, or in parallel. To be asynchronous, simply means the operation is performed when there is time available. Thus, since nothing else appears on the stack, there is, hence the function is executed.

Nick Parsons
  • 45,728
  • 6
  • 46
  • 64
J-Cake
  • 1,526
  • 1
  • 15
  • 35
  • It's extremely surprising and a bit disappointing that operations such as these are not placed on the worker pool, particularly when they are blocking operations on the event loop and yet are clearly written in such a way that it is obvious they are not to be waited for. Is there a way around this? For example, writing the same code (nearly identical in C#) would result in several worker threads executing such tasks as the primary thread continues on. – SpicyMike Aug 01 '21 at 06:33
  • @SpicyMike you can use a worker. Then it'd run in parallel. JS is single threaded by default, parallel execution is an opt-in. – VLAZ Aug 01 '21 at 06:34
  • @SpicyMike I believe the intent behind this design mechanism is to enforce single-threadedness where possible. While it is possible to use them, I find the mechanism to do so is quite inflexible. Plus, for operations like what were detailed in the original question, it seems spawning separate threads is more overhead than gain. – J-Cake Aug 01 '21 at 06:38
  • 1
    Ah I understand. While the main thread is not blocked by promises of non-cpu intensive tasks such as i/o and network communications, it will freeze on the cpu-intensive tasks because the main thread performs all cpu tasks in the promise synchronously when available. That's a bit disappointing, but at least the option to use threads is available. I'll do some more research on that. Thank you both! – SpicyMike Aug 01 '21 at 06:43
  • @JacobSchneider One common use is in heavy datasets - being able to execute processing on every thread is significantly quicker than utilizing a single thread. Imagine processing taking 10 days instead of several hours, or applications that simply freeze during data processing phases. In this example the actual benefit is trivial. It's a question about the concept. The applications of multithreaded processing are out there :). – SpicyMike Aug 01 '21 at 06:50
  • No, I could not agree with you more, my only addition to that would be that in some cases, NodeJS is simply the wrong tool for the job. In fact, if your data processing operations are taking more than a few seconds, then I'd say you should reconsider your tooling altogether. My comment about more overhead than gain was about IO operations which are comparatively slow, but are not repeated excessively. You seem to be talking about data processing, which is a different field altogether, in my opinion. – J-Cake Aug 01 '21 at 07:07