1 year ago

#372023

test-img

Gandalf

Why does calling setTimeout unblocks the event loop in this example?

The following code is from this medium article

const fastify = require("fastify")({ logger: true });
const crypto = require("crypto");
const randomstring = require("randomstring");

// Declare healthcheck ping-pong
fastify.get("/ping", (_request, reply) => {
  reply.send({ data: "pong" });
});

fastify.get("/event-loop-blocking-operation", async (_request, reply) => {
  let hash = crypto.createHash("sha256");
  const numberOfHasUpdates = 10e6;

  for (let iter = 0; iter < numberOfHasUpdates; iter++) {
    hash.update(randomstring.generate());
  }
  reply.send({ data: "Finished doing long task" });
});

fastify.get("/event-loop-nonblocking-operation", async (_request, reply) => {
  const hash = crypto.createHash("sha256");
  const numberOfHasUpdates = 10e6;

  const breathSpace = async (delayInS) =>
    new Promise((resolve, reject) => {
      setTimeout(() => resolve(), delayInS * 1000);
    });

  for (let iter = 0; iter < numberOfHasUpdates; iter++) {
    const hashed = hash.update(randomstring.generate());
    await breathSpace(0);
  }
  reply.send({ data: "Finished long process" });
});

// Run the server!
const port = 3000;
fastify.listen(port, (err, address) => {
  if (err) {
    fastify.log.error(err);
    process.exit(1);
  }
  fastify.log.info(`server listening on ${address}`);
});

case 1

#in Terminal 1
$ node blocking_event_loop.js

#in Terminal 2
#calls 127.0.0.1:3000/ping every one second
#keeps printing {data: "pong"} in terminal
$ while  true; do date; curl 127.0.0.1:3000/ping; sleep 1; echo \n; done;

#in Terminal 3
#blocks the event loop
$ date && curl 127.0.0.1:3000/event-loop-blocking-operation && date

After calling the last command, the event loop gets blocked and Terminal 2 stops printing.

case 2

#in Terminal 1
$ node blocking_event_loop.js

#in Terminal 2
#calls 127.0.0.1:3000/ping every one second
#keeps printing {data: "pong"} in terminal
$ while  true; do date; curl 127.0.0.1:3000/ping; sleep 1; echo \n; done;

#in Terminal 3
#does not block the event loop
$ date && curl 127.0.0.1:3000/event-loop-nonblocking-operation && date

In 2nd case, After calling the last command, Terminal 2 keeps printing {data: "pong"} and the operation defined in the /event-loop-blocking-operation's route handler keeps doing what it's doing.

How is it that calling await breathSpace(0) in the loop will run the operation in a non-blocking way?

As far as I know when the /event-loo-nonblocking-operation endpoint is called via curl:

  1. /event-loop-nonblocking-operation's corresponding route handler is enqueued to the I/O callback queue which is also known as poll queue
  2. route handler is dequeued from poll queue and pushed to call stack
  3. route handler's code is run line by line 3.1 hash.update method is called (on each iteration of for loop) 3.2 await breathSpace(0) is called (on each iteration of for loop) 3.2.1 the (resolve, reject) => setTimeout(() => resolve(), 0) is enqueued to the Timer queue

Now considering event loop not getting blocked, Does this mean, that even though the first iteration of the loop is not done, event loop changes it's phase from poll to timer to handle the setTimeout which resolves the promise? How does it do the for loop without blocking?

javascript

node.js

async-await

event-loop

0 Answers

Your Answer

Accepted video resources