The Node.js event loop coordinates JavaScript callbacks and promise continuations while asynchronous I/O is performed by the operating system or Node worker pool, allowing one process to keep many operations in flight without blocking on each one.
In simpler words
Node can start I/O, work on other requests, and continue when the result is ready — but slow synchronous work still blocks every request sharing that process.
HTTP handlers, database calls, password hashing, and file operations are asynchronous. Understanding await, rejection, and blocking work prevents framework code from hiding runtime-level failures.
Explain what async/await does and does not make asynchronous
Distinguish non-blocking I/O from CPU-heavy synchronous work
Propagate rejected promises to an HTTP error boundary
Avoid fire-and-forget work that loses errors
Promises and async/await
Definition
A Promise represents the eventual fulfillment or rejection of an asynchronous operation; an async function always returns a Promise, and await pauses only that function until the Promise settles.
In simpler words
await makes promise code read in order. It does not freeze Node, but it also does not turn synchronous CPU work into background work.
Returning or awaiting a Promise keeps success and failure connected to the caller.
Independent operations may run concurrently with Promise.all; dependent operations should remain sequential.
Catch an error only when you can add context, recover, or translate it. Otherwise let the framework error boundary handle it.
Awaiting the repository call lets the handler pass rejection to Express error middleware. Nest later performs this translation through its request pipeline and exception filters.
Mistake: detached work
// Wrong — rejection is detached from the request
app.post('/notes', (req, res) => {
notes.save(req.body);
res.status(201).end();
});
// Right — await before claiming success
app.post('/notes', async (req, res, next) => {
try {
const note = await notes.save(req.body);
res.status(201).json(note);
} catch (error) {
next(error);
}
});
Do not report success before the write has succeeded unless an explicit durable background-job design exists.
Blocking the event loop
Definition
Event-loop blocking occurs when long synchronous JavaScript or synchronous native work prevents Node from starting or completing other callbacks in the same process.
In simpler words
One expensive synchronous task can stall every request handled by that process.
Synchronous file reads, huge JSON transformations, unbounded loops, and inappropriate password-hashing settings can delay unrelated requests.
Prefer asynchronous I/O APIs. For truly CPU-heavy work, use bounded worker threads or a background-job system.
Measure before optimizing: latency, event-loop delay, and CPU profiles identify whether the bottleneck is I/O, the database, or CPU work.
Keep in mind
Return or await every request-scoped Promise.
Do not send a success response before the required write succeeds.
Treat synchronous CPU work as shared latency for every request in the process.
Test
Check your understanding
At least 10 questions — mix of concept, syntax, practical, and logic. Score ≥80% (enforced by the API) to save progress.