Intermediate · Asynchronous JavaScript · topic 37 of 61 · 30 min
Promises
Explanation
What a promise is
A promise is a placeholder object for a future value. When you call an async function that returns a promise, you get the promise immediately, and its result fills in later. It is always in one of three states: pending (still waiting), fulfilled (done, with a value) or rejected (failed, with a reason, usually an Error). Once fulfilled or rejected it is settled and never changes again.
Using a promise: then, catch, finally
function getFare(train) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (train === '12951') resolve(3200);
else reject(new Error(`Unknown train ${train}`));
}, 50);
});
}
getFare('12951')
.then((fare) => console.log('Fare:', fare)) // Fare: 3200
.catch((err) => console.log('Error:', err.message))
.finally(() => console.log('Hide spinner'));Creating a promise
new Promise((resolve, reject) => { … }) runs the function immediately. Call resolve(value) to fulfil or reject(error) to reject. Only the first call counts. Most of the time you will USE promises from APIs (fetch, fs/promises) rather than create them, but wrapping an old callback API is a common reason to create one.
function wait(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
wait(100).then(() => console.log('100 ms later'));Chaining
then returns a NEW promise. Whatever your then callback returns becomes the value for the next then. If it returns a promise, the chain waits for it. This turns callback hell into a flat list of steps.
getUser(7)
.then((user) => getOrders(user.id)) // returns a promise; the chain waits
.then((orders) => getInvoice(orders[0].id))
.then((invoice) => sendEmail(invoice))
.then(() => console.log('Done!'))
.catch((err) => console.log('Something failed:', err.message)); // one catch for every stepErrors travel down the chain
If any step rejects or throws, the following thens are skipped until a catch is found. A catch that returns a value "recovers": the chain continues as fulfilled after it.
Promise.resolve(10)
.then((n) => { throw new Error('boom'); })
.then(() => console.log('skipped'))
.catch((e) => { console.log('caught', e.message); return 0; })
.then((n) => console.log('continues with', n));
// caught boom
// continues with 0Turning callbacks into promises
function readFileAsync(path) {
return new Promise((resolve, reject) => {
require('node:fs').readFile(path, 'utf8', (err, text) => (err ? reject(err) : resolve(text)));
});
}
// Node already provides this: require('node:fs/promises').readFile, and util.promisify for others.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
Promises · 1/7
In this lesson you will use promises, objects that stand for a result that arrives later.
Prefer a video by a teacher? Find free videos on “javascript promises then catch chaining explained” on YouTube ↗
In real life: Checking out on a shopping app
Checkout involves several async steps: reserve stock, charge the payment, create the order. With a promise chain each step waits for the previous one, and a single catch shows "Payment failed, nothing was charged" if any step fails.
const step = (name, ok = true) => new Promise((res, rej) => setTimeout(() => (ok ? res(name) : rej(new Error(name + ' failed'))), 10));
step('reserve')
.then(() => step('charge', false))
.then(() => step('create order'))
.then(() => console.log('Order placed'))
.catch((e) => console.log(e.message)); // charge failedPractice (0/3 done)
Write wait(ms) that returns a promise that fulfils (with undefined) after ms milliseconds.
Write checkPin(pin) that returns a promise: it fulfils with "ok" when pin is a 4-digit string, and rejects with an Error "Invalid PIN" otherwise.
Fix the chain in totalForUser: the second then does not return, so the total arrives too late. getUser and getOrders return promises.
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
- 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
- async/await and error handlingFrontend Web Development · Async JavaScript, modules and tools
- Exceptions: try, catch and finallyJava Programming (Core to Spring Boot) · Exceptions and generics
- Exceptions: try, catch, finally and custom exceptionsC# 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.
What are the three states of a promise?
Answer: pending, fulfilled, rejected
A promise is pending until it is fulfilled or rejected.
Promise.resolve(2).then((n) => n * 3).then((n) => n + 1) fulfils with…
Answer: 7
2 × 3 = 6, then 6 + 1 = 7.
If a then callback throws, what runs next?
Answer: The nearest catch
A throw turns into a rejection that skips to catch.
new Promise((res) => { res(1); res(2); }) fulfils with…
Answer: 1
Only the first resolve or reject counts.
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.
In an error-first callback (err, data), what should you do first?Related
Three independent requests each take about 1 second. How long does await Promise.all([a(), b(), c()]) take?Related
Answer: About 1 second
Promise.all starts them together, so the total is about the slowest one. Awaiting them one by one would take about 3 seconds.
How do you handle a rejected await?Related
Answer: try/catch around the await
A rejection is thrown at the await, so catch it.
From async and await
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
- 4 more topics in “Asynchronous JavaScript” before its exam: 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.