Intermediate · Async JavaScript, modules and tools · topic 40 of 64 · 35 min
Promises and the event loop
Explanation
JavaScript does one thing at a time
JavaScript in a web page runs on a single thread. A thread is one line of work: the browser runs your code one statement at a time, never two at once. Most code is fast, so this is fine.
Some jobs are slow: downloading data from a server, waiting for a timer, waiting for the user to click. If JavaScript stood still until these finished, the page would freeze: no scrolling, no typing. So these jobs are asynchronous (async): you start them, your code carries on, and you get the result later.
The event loop in simple words
- Call stack: the code running right now. Only one piece at a time.
- Browser helpers (Web APIs): the browser does the waiting for timers and network requests, outside your code.
- Task queue: finished jobs such as a timer callback or a click handler wait here in a line.
- Microtask queue: promise callbacks (then, catch, finally, code after await) wait here. They are special: they all run as soon as the stack is empty, before the next task.
- Event loop: a simple loop. When the call stack is empty, run all microtasks, then take the next task from the queue, and repeat.
console.log('A');
setTimeout(() => console.log('B'), 0); // a task
Promise.resolve().then(() => console.log('C')); // a microtask
console.log('D');
// Output: A, D, C, BCallbacks and callback hell
You met callbacks in beginner JavaScript: a function you pass to another function, to be called later. Before promises, every async job took a callback. When one step needs the result of the step before, the callbacks nest deeper and deeper.
getUser(userId, (user) => {
getOrders(user.id, (orders) => {
getOrderItems(orders[0].id, (items) => {
showItems(items);
// and errors? each level needs its own error check...
});
});
});Promises: a value that comes later
| State | Meaning | What runs |
|---|---|---|
| pending | Still waiting; no result yet | Nothing yet |
| fulfilled | Finished with a value (resolved) | then callbacks |
| rejected | Failed with a reason, usually an Error | catch callbacks |
A promise starts pending and settles only once: it becomes fulfilled or rejected, and then it never changes again. finally runs in both cases.
function wait(ms) {
return new Promise((resolve) => {
setTimeout(() => resolve(ms), ms);
});
}
wait(1000).then((ms) => console.log(`Waited ${ms} ms`));showSpinner();
getUserPromise(42)
.then((user) => getOrdersPromise(user.id)) // return the next promise
.then((orders) => showOrders(orders))
.catch((error) => showError(error.message)) // any step that fails lands here
.finally(() => hideSpinner()); // runs on success and on failureWaiting for many promises
| Method | Settles when | Use it when |
|---|---|---|
| Promise.all(list) | All fulfil (gives an array of values), or the first one rejects | You need every result, e.g. user + cart + offers for one page |
| Promise.allSettled(list) | All have settled; gives { status, value or reason } for each | You want every result even if some fail, e.g. 5 shops' prices |
| Promise.race(list) | The first one settles, success or failure | A timeout: race the request against a timer |
| Promise.any(list) | The first one fulfils; rejects only if all fail (AggregateError) | Several mirrors of the same data; take the fastest that works |
const [user, cart] = await Promise.all([
getUserPromise(42),
getCartPromise(42),
]);
console.log(user.name, cart.items.length);Watch the lesson
Promises and the event loop · 1/7
JavaScript in a page does one thing at a time. So how does it wait for a server without freezing?
Prefer a video by a teacher? Find free videos on “javascript promises event loop explained for beginners” on YouTube ↗
In real life: A train search page
On a railway search page, you pick two stations and press Search. The page shows a spinner, starts three requests together with Promise.all (trains, fares and seat availability) and stays smooth while it waits. When all three arrive, it draws the list; if any one fails, catch shows "Could not load trains, please try again", and finally hides the spinner in both cases.
Practice (0/3 done)
Write a function wait(ms) that returns a Promise. The promise must resolve after ms milliseconds (use setTimeout), with the number ms as its value. Example: await wait(40) waits about 40 ms and gives 40.
You ask several shops for the price of the same item. Some shops fail. Write async function summarize(promises) that waits for ALL of them with Promise.allSettled and returns { ok, failed }: ok is the list of values of the fulfilled promises, failed is the list of error messages (reason.message) of the rejected ones, both in the original order. Example: [price 120, error "Shop closed", price 80] gives { ok: [120, 80], failed: ["Shop closed"] }.
Predict, then check. On paper, write the order in which these lines print: console.log(1); setTimeout(() => console.log(2), 0); Promise.resolve().then(() => console.log(3)); wait(10).then(() => console.log(4)); console.log(5). (wait is your helper from task 1.) Then paste the code into the DevTools Console and compare. Explain each line in one sentence using the words call stack, microtask and task.
Quick quiz
Read more (free)
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.