Asynchronous Programming and the Event Loop
Master callbacks, Promises, async/await, the Node.js event loop, microtasks, timers, and non-blocking I/O.
Callbacks, Promises, and async/await
Node.js APIs frequently perform asynchronous operations. Older APIs commonly use callbacks, while modern APIs increasingly return Promises.
async and await provide a readable syntax for consuming Promises. An async function always returns a Promise, and await pauses that function until the Promise settles without blocking the Node.js JavaScript thread.
async function getUser() {
const response = await fetch('[https://example.com/user');
](https://example.com/user'%29;
) return response.json();
}
getUser()
.then(console.log)
.catch(console.error);await makes asynchronous code easier to read while the underlying operation remains asynchronous.- Promises represent eventual completion or failure of asynchronous operations.
- An
asyncfunction returns a Promise. - Use
try/catcharound awaited operations when local error handling is required.
What does an async function return?
How the Node.js Event Loop Works
Node.js uses an event-driven architecture. JavaScript executes on the main thread while asynchronous work can be handled by the operating system or libuv. Once asynchronous operations complete, callbacks are scheduled for execution.
The event loop contains phases such as timers, pending callbacks, poll, check, and close callbacks. Understanding these phases helps explain the behavior of timers, sockets, and asynchronous I/O.
Node.js executes JavaScript on a main thread, but asynchronous I/O and libuv worker threads allow many operations to progress concurrently.
- The event loop coordinates asynchronous callbacks.
- Non-blocking I/O allows a Node.js process to handle many concurrent connections.
- CPU-heavy JavaScript still blocks the event loop unless moved elsewhere.
What happens when CPU-heavy synchronous JavaScript runs on the event-loop thread?
Microtasks, process.nextTick, and setImmediate
Node.js has multiple queues with different scheduling behavior. Promise callbacks and other microtasks can execute before the event loop proceeds to later phases. process.nextTick() has especially high priority in Node.js and should not be recursively scheduled without care because excessive use can starve I/O.
setImmediate() schedules work for the check phase, while timers such as setTimeout() execute in the timers phase when their timing conditions are met.
console.log('start');
setTimeout(() => console.log('timeout'), 0);
setImmediate(() => console.log('immediate'));
Promise.resolve().then(() => console.log('promise'));
process.nextTick(() => console.log('nextTick'));
console.log('end');nextTick, Promise microtasks, timers, and immediates.- Synchronous JavaScript runs before asynchronous callbacks.
- Promise callbacks are microtasks.
process.nextTick()should be used carefully because it can starve the event loop.
Which mechanism is specifically a Node.js next-tick queue mechanism?