6.4 — Promises
Last lesson's pain, prescribed a cure. Instead of you handing your next step INTO the async function (a callback), the function hands YOU something back: a promise — a receipt for work in progress. Order a burger, get a receipt immediately. The receipt isn't the burger — it's a live object that will flip when the kitchen finishes, and you can attach plans to it: "when ready, do this; if it fails, do that."
Three states, one flip: that's the whole shape of a promise.
Flat chains and a single shared error drain follow from it — by the end of this lesson the pyramid of doom is a straight line, and you'll have built a promise with your own hands.
const order = new Promise((resolve, reject) => {
setTimeout(() => {
resolve("burger #42");
}, 60);
});
console.log("receipt in hand");
order
.then((food) => {
console.log("eating " + food);
return food + " + fries";
})
.then((meal) => {
console.log("upgraded: " + meal);
})
.catch((err) => {
console.log("refund: " + err.message);
});new Promise((resolve, reject) => { …start the work… }) — the executor function runs IMMEDIATELY and starts the async work (here, a timer). What you get back, instantly, is the receipt: a promise in state PENDING. Two levers are handed to the executor: resolve(value) to flip it to success, reject(error) to flip it to failure.
The deeper story, with the real names for things — this part is what turns “I saw it” into “I can explain it.”
A promise is a plain object with internal state (pending / fulfilled / rejected) plus lists of reactions to run on settle.
Precision that impresses: .then doesn't "wait" — it registers and immediately returns the next promise.
Attach a .then to an already-settled promise and it still runs (soon, not instantly — the exact "when" is next lesson's microtask story).
You'll consume promises far more often than construct them. fetch (6.7) returns one; every Playwright action returns one. Your test code will be promise-chains written in 6.6's async/await syntax. Building one by hand today (the exercise) is like taking apart a lock: afterward, every key makes sense.
Naming note for the wild: people say "resolved" loosely to mean fulfilled; strictly, settled = no longer pending (either outcome), fulfilled = success, rejected = failure.
Use the strict words in interviews; understand the loose ones in blog posts.
⌨️ build the receipt machine
Make your own promise from scratch — then transform its value through a FLAT two-link chain (no nesting anywhere).
requirements:
- A function
delayValue(ms, value)that returns anew Promisewhich resolves withvalueaftermsmilliseconds (setTimeout inside the executor). - Call
delayValue(50, 10)and attach a chain: the first.thendoubles the value and RETURNS it; the second.thenadds 1 and prints the result. - Output must be
21— and your chain must be flat: two .then links in a row, no callback inside a callback.
when you press RUN, the console must show exactly:
✏️ Quick check 1
A new Promise whose executor’s resolve fires in 5 seconds — what STATE is it in right after creation? Type it.
✏️ Quick check 2
Type exactly what this prints:
Promise.resolve(5)
.then((n) => {
return n + 1;
})
.then((n) => {
console.log(n * 10);
});✏️ Quick check 3
The promise a chain STARTS from rejects. The chain has three .then links after it and one final .catch. How many of the .then links run? Type the number.
🗣️ Now teach it back
Explain promises to a friend using the receipt picture: the three states and the one-flip rule, what .then really does (and returns!), how chains stay flat, and where errors go.
Write it as if your friend is sitting next to you. Saved to your journal — future-you will use these notes to teach others.