Event Loop Basics
9 examples to get you started with the Event Loop - 6 basic and 3 intermediate.
Search across all documentation pages
9 examples to get you started with the Event Loop - 6 basic and 3 intermediate.
Synchronous code runs to completion before any scheduled callback.
console.log('1');
console.log('2');
console.log('3');
// Output: 1, 2, 3Related: How Node.js Works - V8 and the single thread
setTimeout does not run immediately - it queues a callback for a later loop iteration.
console.log('start');
setTimeout(() => console.log('timeout'), 0);
console.log('end');
// start, end, timeout0 ms is a minimum delay, not a guarantee - the loop must clear first.setTimeout and its fire delays the callback further.Related: Timers & Scheduling - drift under load
Microtasks (Promise .then) drain before the next macrotask.
console.log('A');
setTimeout(() => console.log('B'), 0);
Promise.resolve().then(() => console.log('C'));
console.log('D');
// A, D, C, BPromise.resolve().then schedules a microtask on the microtask queue.Related: Microtasks vs Macrotasks - full ordering rules
await suspends the async function and resumes via microtasks.
async function demo(): Promise<void> {
console.log('before');
await Promise.resolve();
console.log('after');
}
demo();
console.log('sync');
// before, sync, afterawait runs as a microtask continuation.after.await chains stack microtasks - keep chains shallow when possible.Related: async/await in Node - error handling patterns
fs.promises schedules I/O on libuv and returns control immediately.
import { readFile } from 'node:fs/promises';
console.log('before read');
readFile('package.json', 'utf8').then((data) => {
console.log('read', data.length, 'bytes');
});
console.log('after read');.then callback runs after the file read completes on a background thread.before and after runs while I/O is in flight.readFileSync would block the loop - avoid it in request paths.setImmediate runs in the check phase, after I/O polling.
import { setImmediate } from 'node:timers';
setTimeout(() => console.log('timeout'), 0);
setImmediate(() => console.log('immediate'));
// Order varies at top level; inside I/O callbacks, immediate usually winssetTimeout(0) and setImmediate is not guaranteed.setImmediate typically runs before the next timer.queueMicrotask or setImmediate over setTimeout(fn, 0) for deferral.Related: libuv Phases - timers, poll, check phases
CPU work on the main thread stalls all other callbacks.
import { createServer } from 'node:http';
function block(ms: number): void {
const end = Date.now() + ms;
while (Date.now() < end) { /* spin */ }
}
createServer((req, res) => {
if (req.url === '/block') block(3000);
res.end('ok');
}).listen(3000);block runs, no other requests progress in this process.worker_threads or a job queue.Related: Detecting Event-Loop Blockage - measure lag in production
nextTick runs before other microtasks - use sparingly.
Promise.resolve().then(() => console.log('promise'));
process.nextTick(() => console.log('nextTick'));
// nextTick, promisenextTick queue drains before Promise microtasks.nextTick can starve I/O - never loop with nextTick.queueMicrotask for deferral unless you need nextTick semantics.Related: Microtasks vs Macrotasks - nextTick ordering
perf_hooks.eventLoopUtilization reports how busy the loop is.
import { eventLoopUtilization } from 'node:perf_hooks';
const start = eventLoopUtilization();
setInterval(() => {
const elu = eventLoopUtilization(start);
console.log('utilization:', elu.utilization.toFixed(3));
}, 5000);Related: Event Loop Best Practices - operational rules
Stack versions: This page was written for Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5, and NestJS 11.
Reviewed by Chris St. John·Last updated Jul 16, 2026