Intermediate · Asynchronous JavaScript · topic 36 of 61 · 25 min
Why async, and callbacks
Explanation
One thread, many waits
A thread is one line of work that runs instructions one after another. JavaScript runs your code on a single main thread. If that thread stopped for two seconds waiting for a server, the whole page would freeze: no scrolling, no clicks. Node.js would stop serving every other user during the wait.
So slow operations are asynchronous (async): you START them, your code continues immediately, and you are told LATER when they finish. The waiting happens outside your code, in the browser or in Node's system layer.
Synchronous versus asynchronous
console.log('1. Order placed');
setTimeout(() => {
console.log('3. SMS sent'); // runs later
}, 1000);
console.log('2. Showing receipt');Callbacks
A callback is a function you pass to another function so that it can call you back when something happens. You have already used callbacks with map and sort; there they are called immediately. With async APIs they are called later.
function fakeFetchUser(id, callback) {
setTimeout(() => callback({ id, name: 'Asha' }), 100);
}
fakeFetchUser(7, (user) => console.log('Got', user.name));
console.log('Request sent');Error-first callbacks in Node
Older Node.js APIs use a convention: the callback's first argument is an error (or null), and the result comes second. You must always check the error first.
const fs = require('node:fs');
fs.readFile('orders.csv', 'utf8', (err, text) => {
if (err) {
console.error('Could not read file:', err.message);
return;
}
console.log('Lines:', text.split('\n').length);
});Callback hell
When each step needs the result of the previous one, callbacks nest inside callbacks. The code drifts to the right, errors must be handled at every level, and the flow is hard to follow. This shape is called callback hell or the pyramid of doom.
getUser(id, (err, user) => {
if (err) return fail(err);
getOrders(user.id, (err, orders) => {
if (err) return fail(err);
getInvoice(orders[0].id, (err, invoice) => {
if (err) return fail(err);
sendEmail(user.email, invoice, (err) => {
if (err) return fail(err);
console.log('Done!');
});
});
});
});There is also a trust problem called inversion of control: you hand your function to someone else's code and hope it calls it exactly once, not twice, not never. Promises (next topic) solve both problems.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Why async, and callbacks · 1/6
In this lesson you will see why JavaScript needs asynchronous code, and meet callbacks.
Prefer a video by a teacher? Find free videos on “javascript callbacks asynchronous explained callback hell” on YouTube ↗
In real life: A reminder service
A clinic booking system sends a reminder 30 minutes before each appointment. It does not loop and wait; it schedules a callback with a timer for each appointment and goes on serving other requests. When the timer fires, the callback sends the SMS.
function scheduleReminder(patient, msFromNow, send) {
setTimeout(() => send(`Reminder for ${patient}`), msFromNow);
}
scheduleReminder('Meena', 50, (text) => console.log(text));
console.log('Booking saved');Practice (0/2 done)
Write delayedDouble(n, callback) that calls callback(n * 2) after about 10 milliseconds using setTimeout.
Write parseLater(text, callback) using the error-first style: after ~5 ms, call callback(null, value) with the parsed JSON, or callback(error) if JSON.parse throws.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- PromisesAsynchronous JavaScript · same module
- async and awaitAsynchronous JavaScript · same module
- Promise.all, allSettled, race and anyAsynchronous JavaScript · same module
- Promises and the event loopFrontend Web Development · Async JavaScript, modules and tools
- Node.js: JavaScript on the serverBackend Development · Node.js and Express
- async and await: waiting without blockingC# and .NET · Exceptions, delegates, events and async
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
console.log('A'); setTimeout(() => console.log('B'), 0); console.log('C'); What is printed?
Answer: A C B
Even with 0 ms, the timer callback runs after the current code finishes.
In an error-first callback (err, data), what should you do first?
Answer: Check err
If err is set, data is not valid.
Why is JavaScript code asynchronous for network calls?
Answer: So the single thread is not blocked while waiting
Blocking the main thread would freeze the page or server.
What is "callback hell"?
Answer: Deeply nested callbacks that are hard to read and handle errors in
It describes the pyramid shape of nested async callbacks.
What does this print? console.log('A'); setTimeout(() => console.log('B'), 0); Promise.resolve().then(() => console.log('C')); console.log('D');Related
Answer: A D C B
Normal code runs first (A, D). Then the microtask queue runs (C), and only then the timer task (B), even with 0 ms.
Which queue do promise then callbacks use?Related
If a then callback throws, what runs next?Related
Why can one Node.js process serve many users at once?Related
Answer: It does not wait idle for slow I/O; it serves other requests meanwhile
With non-blocking I/O, Node.js starts a slow job and serves other requests until the result is ready, like one waiter serving many tables.
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 5 more topics in “Asynchronous JavaScript” before its exam: Promises, async and await, Promise.all, allSettled, race and any, The event loop, fetch: talking to servers.
- Module 8 exam: Asynchronous JavaScript · 12 questions · 25 min, without help. 6 topics of this module still to finish first.
- Then the next module, TypeScript basics. 61 topics left in the course before the final exam.