Async node
when the cb will be executed?
timers: this phase executes callbacks scheduled by setTimeout() and setInterval().
pending callbacks: executes I/O callbacks deferred to the next loop iteration.
idle, prepare: only used internally.
poll: retrieve new I/O events; execute I/O related callbacks
check: setImmediate() callbacks are invoked here.
close callbacks: some close callbacks, e.g. socket.on('close', ...).
setTimeout(() => console.log("timeout"));
Promise.resolve()
.then(() => console.log("promise"));
console.log("main");
Only after tasks in microTasks are completed, event loop will next pick up tasks from macroTasks.
function logA() { console.log('A') };
function logB() { console.log('B') };
function logC() { console.log('C') };
function logD() { console.log('D') };
function logE() { console.log('E') };
function logF() { console.log('F') };
function logG() { console.log('G') };
function logH() { console.log('H') };
function logI() { console.log('I') };
function logJ() { console.log('J') };
logA();
setTimeout(logG, 0);
Promise.resolve()
.then(logC)
.then(setTimeout(logH))
.then(logD)
.then(logE)
.then(logF);
setTimeout(logI);
setTimeout(logJ);
logB();
const messageQueue = [];
let sendMessage = message => {
messageQueue.push(message);
if (messageQueue.length === 1) {
queueMicrotask(() => {
const json = JSON.stringify(messageQueue);
messageQueue.length = 0;
fetch("url-of-receiver", json);
});
}
};
console.log(1)
setTimeout(() => {
queueMicrotask(() => {
console.log(2)
});
console.log(3)
});
Promise.resolve().then(() => console.log(4))
queueMicrotask(() => {
console.log(5)
queueMicrotask(() => {
console.log(6)
});
});
console.log(7)
Just remember — sync tasks → micro tasks → macro tasks
Generally, it's about capturing or checking results, or performing cleanup, after the main body of a JavaScript execution context exits, but before any event handlers, timeouts and intervals, or other callbacks are processed.
setImmediate(callback[, ...args]) takes a callback and add it to the event queue( specifically the immediate queue).
console.log('Start')
setImmediate(() => console.log('Queued using setImmediate'))
console.log('End')
setTimeout(() => {
console.log('timeout');
}, 0);
setImmediate(() => {
console.log('immediate');
});
process.nextTick(callback[, ...args]) will also takes a callback and optional args parameters like setImmediate() function. But instead of "immediate queue", the callbacks are queued in the "next tick queue".
console.log('Start')
process.nextTick(() => console.log('Queued using process.nextTick'))
console.log('End')
It allows you to "starve" your I/O by making a recursive process.nextTick() calls, which prevents the event loop from reaching the poll phase.
There are two main reasons:
Allow users to handle errors, cleanup any then unneeded resources, or perhaps try the request again before the event loop continues.
At times it's necessary to allow a callback to run after the call stack has unwound but before the event loop continues
Promise.resolve().then(() => { console.log('1'); });
process.nextTick(() => {
console.log('2.');
process.nextTick(() => {
console.log('3');
process.nextTick(() => {
console.log('3');
process.nextTick(() => {
console.log('5');
})
})
})
})
let count = 0
const cb = () => {
console.log(`Processing setImmediate cb ${++count}`)
setImmediate(cb)
}
setImmediate(cb)
setTimeout(() => console.log('setTimeout executed'), 100)
console.log('Start')
We recommend developers use setImmediate() in all cases because it's easier to reason about (and it leads to code that's compatible with a wider variety of environments, like browser JS.)
If we are running this heavy CPU bound operation in the context of a web application,the single thread of node will be blocked and hence the webserver won't be able to respond to any request because it is busy calculating results.
const express = require("express")
const app = express()
app.get("/getfibonacci", (req, res) => {
const startTime = new Date()
const result = fibonacci(parseInt(req.query.number)) //parseInt is for converting string to number
const endTime = new Date()
res.json({
number: parseInt(req.query.number),
fibonacci: result,
time: endTime.getTime() - startTime.getTime() + "ms",
})
})
const fibonacci = n => {
if (n <= 1) {
return 1
}
return fibonacci(n - 1) + fibonacci(n - 2)
}
app.listen(3000, () => console.log("listening on port 3000"))
const express = require("express")
const app = express()
const { fork } = require("child_process")
app.get("/isprime", (req, res) => {
const childProcess = fork("./forkedchild.js") //the first argument to fork() is the name of the js file to be run by the child process
childProcess.send({ number: parseInt(req.query.number) }) //send method is used to send message to child process through IPC
const startTime = new Date()
childProcess.on("message", message => {
//on("message") method is used to listen for messages send by the child process
const endTime = new Date()
res.json({
...message,
time: endTime.getTime() - startTime.getTime() + "ms",
})
})
})
// child.js
//
process.on("message", message => {
//child process is listening for messages by the parent process
const result = isPrime(message.number)
process.send(result)
process.exit() // make sure to use exit() to prevent orphaned processes
})
function isPrime(number) {
let isPrime = true
for (let i = 3; i < number; i++) {
if (number % i === 0) {
isPrime = false
break
}
}
return {
number: number,
isPrime: isPrime,
}
}
const {
Worker, isMainThread, parentPort, workerData
} = require('worker_threads');
if (isMainThread) {
module.exports = function parseJSAsync(script) {
return new Promise((resolve, reject) => {
const worker = new Worker(__filename, {
workerData: script
});
worker.on('message', resolve);
worker.on('error', reject);
worker.on('exit', (code) => {
if (code !== 0)
reject(new Error(`Worker stopped with exit code ${code}`));
});
});
};
} else {
const { parse } = require('some-js-parsing-library');
const script = workerData;
parentPort.postMessage(parse(script));
}
import { parentPort } from 'worker_threads';
parentPort.on('message', () => {
const numberOfElements = 100;
const sharedBuffer = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * numberOfElements);
const arr = new Int32Array(sharedBuffer);
for (let i = 0; i < numberOfElements; i += 1) {
arr[i] = Math.round(Math.random() * 30);
}
parentPort.postMessage({ arr });
});
In order for the memory to be shared, an instance of ArrayBuffer or SharedArrayBuffer must be sent to the other thread as the data argument or inside the data argument.
const cluster = require("cluster")
const http = require("http")
const cpuCount = require("os").cpus().length //returns no of cores our cpu have
if (cluster.isMaster) {
masterProcess()
} else {
childProcess()
}
function masterProcess() {
console.log(`Master process ${process.pid} is running`)
//fork workers.
for (let i = 0; i < cpuCount; i++) {
console.log(`Forking process number ${i}...`)
cluster.fork() //creates new node js processes
}
cluster.on("exit", (worker, code, signal) => {
console.log(`worker ${worker.process.pid} died`)
cluster.fork() //forks a new process if any process dies
})
}
function childProcess() {
const express = require("express")
const app = express()
//workers can share TCP connection
app.get("/", (req, res) => {
res.send(`hello from server ${process.pid}`)
})
app.listen(5555, () =>
console.log(`server ${process.pid} listening on port 5555`)
)
}