Node js server stops listening until promise (async function which runs a loop, hoping to make this loop run in background) is resolved

Viewed 71

This is my server code which is listening on the port

app.listen(PORT, () => {
          console.log(`Server running on ${PORT}`);
          });

Here I am receiving a request and sending it to the function to process

        app.get("/",(req, res) => {
          res.header("Access-Control-Allow-Origin", "*");
          console.log("before call");
          callback(100,1000000000000).then(response=>console.log(response));
          console.log("after call");
          res.end();
        });

This is the function that is processing incoming request

        async function callback(first, second) {
           await null;
           for (let i = first; i < second; i++) {
             if (isPrime(i)) {
                //writes to file if the number is prime
             }
           }
1 Answers

Node.js executes most of the operations on one thread, the main thread, but it can use multiple threads, by spawning child processes, or by using the worker thread. You can read a lot about worker thread and child processes on docs: https://nodejs.org/api/worker_threads.html https://nodejs.org/api/child_process.html#child-process A worker thread is particularly useful in scenarios like yours, where you have heavy synchronous tasks to be executed and you don't want them to block the main thread. If you have async I/O tasks like file reading/ writing, which have both sync and async API etc... it's better using the built-in async methods, since they are more performant.

Worker threads, like child processes communicate with the main thread the usual node way, through events that are emitted in the form of messages, and listeners. I'll give you an example here, this is a simple Node web server, when it receives a request, it will respawn a worker thread and send it a message with two numbers and a type. From now on the main thread remains free, and all the computation is performed on the worker thread. When the worker thread has done to compute all prime numbers and write them in a file, it notifies the main thread, and the main thread terminates the worker. The difference with your case, is that in this case the main thread is not blocked, and other client requests may by processed as usual in the meanwhile:

// App.js

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

const hostname = "127.0.0.1";
const port = 5001;

const server = http.createServer(serverHandler);

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

function serverHandler(req, res) {
  try {
    const worker = new Worker("./worker-thread.js"); // Respawn a worker
    console.log("BEFORE HEAVY WORK");
    worker.on("message", (result) => {
      console.log(`Message received in parent thread : ${result.message}`);
      if (result.message === "SUCCESS") {
        console.log("WORKER RESULT:", result.message);
        worker.terminate();
      }
    });
    worker.on("exit", (code) => {
      console.log(`worker exited with code ${code}`);
    });
    worker.postMessage({
      type: "calculate_primes",
      payload: { first: 1, second: 100000 },
    });
    console.log("MAIN THREAD REMAINED FREE");
    res.end("Your request is being processed correctly");
  } catch (e) {
    console.log(e);
  }
}

This is the "worker-thread.js" file:

const { parentPort } = require("worker_threads");
const fs = require("fs");

function calculatePrime({ first, second }) {
  console.log("First number:", first, "Second number:", second);
  const stream = fs.createWriteStream("result.txt", { flags: "a" });
  const result = [];
  for (let i = first; i < second; i++) {
    if (isPrime(i)) {
      result.push(i);
    }
  }
  stream.write(JSON.stringify(result));

  return stream.end();
}

parentPort.on("message", (message) => {
  try {
    console.log("MESSAGE RECEIVED ON WORKER THREAD", message);
    if (message.type === "calculate_primes") {
      console.log("STARTING HEAVY WORK ON WORKER THREAD");
      // CPU HEAVY TASK
      calculatePrime(message.payload);
      // SEND CONFIRM MESSAGE BACK TO MAIN THREAD WHEN FINISHED
      parentPort.postMessage({ message: "SUCCESS" });
    }
  } catch (e) {
    console.log(e);
  }
});

function isPrime(num) {
  for (let i = 2; i < num; i++) if (num % i === 0) return false;
  return num > 1;
}

Test it just by executing App.js and going to 127:0.0.1:5001 on your web browser. You should receive the response within half a second, but the worker will keep processing your isPrime loop and will write to result.txt the result when done.

Related