1

I'm trying to make a function call some code while allowing my main function to continue and not have to wait for it.

So far it's not working:

function doSomething() {
  return new Promise((resolve, reject) => {
    if(1) {
        console.log("doing something");
        for(let i = 0; i < 10000; i++) {
            for(let j = 0; j < 10000; j++) {
                for(let k = 0; k < 100; k++) {
                 // Do something for a long time to check if async is working
                }
            }
        }
        console.log("finished doing something");
        resolve( {"dummydata": 10} );
    }
    else {
        reject("error!");
    }
  })
}

function main() {
    doSomething()
    .then(data => console.log(data))
    .catch(err => console.error(err))

    console.log("this should print before doSomething() is finished");
}

main();

This is the output:

doing something

finished doing something

this should print before doSomething() is finished

{ dummydata: 10 }

Why is my program waiting for doSomething()? I want it to run in parallel / asynchronously. I also tried making main() an async function, same with doSomething(), but that didn't work.

Ran Shorowitz
  • 307
  • 5
  • 11
  • 1
    since you aren't actually doing anything asynchronous, then your guess at what should happen is actually wrong ... wrap the code inside the promise executor in a setTimeout, then you WILL be doing something asynchronous, not just stating a false assertion – Bravo Feb 14 '22 at 02:09
  • Promises are a utility for dealing with inherently asynchronous tasks. Not for making code run in parallel. – Bergi Feb 14 '22 at 02:57

2 Answers2

2

JavaScript is still single-threaded. If you have an expensive segment of code, like your nested loop, and you don't await inside the loop, all other JavaScript processing in your environment will be blocked while the loop iterates - control flow will only yield back once all the synchronous code has finished.

Code put at the top level of the Promise constructor callback runs immediately - doing return new Promise doesn't make the code be put on a separate thread, or something like that.

While you could queue the task so it starts after a delay, and lets the main script continue whatever it needs to do:

function main() {
  setTimeout(doSomething, 2000);
  // more code

To truly run it in parallel, you'll need to use a child_process. Put the expensive section of the script into its own file, and have child_process invoke it via .fork.

CertainPerformance
  • 356,069
  • 52
  • 309
  • 320
  • No need for a separate process, a worker thread would be enough. – Bergi Feb 14 '22 at 02:59
  • @Bergi wait so then how does app.listen() (app = express()) run code asynchronously on the same thread? Or does it also use a child_process? I'm able to call app.listen(), allow it to listen for events while also simultaneously run other code. How does that work if javascript is single-threaded? – Ran Shorowitz Feb 14 '22 at 03:32
  • 2
    @RanShorowitz Just *listening* for events or messages doesn't require code to be running continuously like in your loop - it just requires the other process to hook into your code, which eventually results in a callback running. If you had a heavy loop in your Express app, multiple requests could see a delay until the prior request's loop finished. – CertainPerformance Feb 14 '22 at 03:51
1

You can use worker_threads: https://nodejs.org/api/worker_threads.html#worker-threads

const { Worker } = require("worker_threads");

const worker = new Worker(
  `
const { parentPort } = require('worker_threads');

parentPort.postMessage("doing something");
  for (let i = 0; i < 10000; i++) {
    for (let j = 0; j < 10000; j++) {
      for (let k = 0; k < 100; k++) {
        // Do something for a long time to check if async is working
      }
    }
  }
  parentPort.postMessage("finished doing something");
  parentPort.postMessage({ dummydata: 10 });

`,
  { eval: true }
);
worker.on("message", (message) => console.log(message));

console.log("this should print before doSomething() is finished");

This is just a snippet. You can exchange data with worker and so on...

valerii15298
  • 737
  • 4
  • 15
  • What's the difference between a worker_thread and a child_process? – Ran Shorowitz Feb 14 '22 at 03:34
  • 1
    from nodejs docs: `Unlike child_process or cluster, worker_threads can share memory.` Also here is this topic: https://stackoverflow.com/questions/56312692/what-is-the-difference-between-child-process-and-worker-threads – valerii15298 Feb 14 '22 at 03:37
  • Ah. Also the code you posted doesn't appear to be running asynchronously? The program appears to be waiting for the worker to finish before doing anything else. – Ran Shorowitz Feb 14 '22 at 07:53
  • @RanShorowitz worker's code is executed in parallel. What do you mean saying `asynchronously`? Actually, JavaScript is single-threaded so all async functions are executed on the same thread*(see link in the bottom). And while using threads you can do anything you want after worker's code is running in parallel unlike js async where your program will lag. And when you run code above, node.js will wait for all workers to end execution. Since the script above is not doing much, after calling worker, so nodejs will wait for worker to finish. https://stackoverflow.com/a/61134366/11963457 – valerii15298 Feb 14 '22 at 20:45