JavaScript Promises
The cup is a paragraph, #cup. It starts as blank, and the script sets it to waiting before it asks for tea.
pour does not hand back the drink. It hands back a promise. You ask that promise, with then, to write the drink later. The line under pour still says waiting. The line reads waiting / lida tea.
Not the cup you just served
The previous page passed done into serve. serve called it before returning, so the log underneath was already lida tea / poured. Nothing on that page waited.
Here the function you pass to then does not run on the next line. pour returns first. The drink shows up after this script has finished.
Three states
A promise is pending until it settles. resolve fulfills it with a value. reject rejects it with a reason. then runs only for a fulfillment. catch runs only for a rejection. This page’s kettle has no timer. The executor calls resolve or reject immediately, and the handlers still wait until the script ends.
📊 Three states
| State | What you see | Easy to mix up |
|---|---|---|
| pending | the cup still says waiting | the drink is already on the cup |
| fulfilled | then receives lida tea | catch runs as well |
| rejected | catch receives no leaves | then receives the drink |
The executor is now, the handler is not
The function you give new Promise runs at once. That is why resolve("lida tea") can happen before pour returns. The function you give then is different. It is queued. The line after pour(...) still runs while the cup says waiting.
The next line is still waiting
pour("lida") stores a promise in order. The cup was set to waiting. The log is that word, not the drink.
function pour(order) {
return new Promise(function (resolve, reject) {
if (order === "") {
reject("no leaves");
return;
}
resolve(order + " tea");
});
}
let cup = document.getElementById("cup");
cup.textContent = "waiting";
let order = pour("lida");
console.log(cup.textContent);
The log is waiting. order is the promise. It is not the string lida tea.
then receives the drink
Call then with a function. That function gets the value passed to resolve. It writes the drink and logs it. This log is not the one on the next line. It is the one inside then.
function pour(order) {
return new Promise(function (resolve, reject) {
if (order === "") {
reject("no leaves");
return;
}
resolve(order + " tea");
});
}
let cup = document.getElementById("cup");
pour("lida").then(function (drink) {
cup.textContent = drink;
console.log(cup.textContent);
});
The log is lida tea. An empty string never reaches this handler.
No leaves
pour("") calls reject("no leaves") and returns. catch receives that reason. then does not run.
function pour(order) {
return new Promise(function (resolve, reject) {
if (order === "") {
reject("no leaves");
return;
}
resolve(order + " tea");
});
}
pour("").catch(function (reason) {
console.log(reason);
});
The log is no leaves.
The promise is not the drink
order.then is a function. That is how you know order is a promise and not the string you wanted. Comparing the promise to "lida tea" would be false, and this snippet only prints the type of then.
function pour(order) {
return new Promise(function (resolve, reject) {
if (order === "") {
reject("no leaves");
return;
}
resolve(order + " tea");
});
}
let order = pour("lida");
console.log(typeof order.then);
The log is function.
The line under the cup
The script sets the cup to waiting and pushes that word at once. then pushes lida tea and joins the two words. The join is inside then, so it happens after the script, not on the next line.
<!DOCTYPE html>
<html>
<head>
<title>promised-cup — page</title>
</head>
<body>
<p id="cup">blank</p>
<p id="line">…</p>
<script>
function pour(order) {
return new Promise(function (resolve, reject) {
if (order === "") {
reject("no leaves");
return;
}
resolve(order + " tea");
});
}
let cup = document.getElementById("cup");
let seen = [];
cup.textContent = "waiting";
seen.push(cup.textContent);
pour("lida").then(function (drink) {
seen.push(drink);
cup.textContent = drink;
document.getElementById("line").textContent = seen.join(" / ");
});
</script>
</body>
</html>
The line reads waiting / lida tea. Waiting was immediate. lida tea arrived in then. The word blank is gone. lida tea / poured is the previous page.
At the counter
- Set the cup to waiting, call
pour("lida"), and log the cup at once. The log is waiting. - Put the drink on the cup inside
then. That log is lida tea. - Call
pour("")andcatchthe reason. The log is no leaves. - Read
typeof order.then. The log is function. - Read the page line: waiting / lida tea.
Misses at the counter
The cup says waiting until then runs. It does not say poured.
- Expecting the log under
pourto be lida tea - Treating the return value as the drink string
- Using
thenfor an empty order and missing no leaves - Using
catchfor lida and expecting no leaves - Reading the line as lida tea / poured
Keep this
pourreturns a promise, not the string lida tea- The next line still logs waiting
thenreceives lida tea after this script- An empty order rejects with no leaves, and
catchlogs it - The page line reads waiting / lida tea
🧠 Test Your Knowledge
Test Your Knowledge
Challenge yourself with this interactive quiz and see how well you understand the topic
📝 Instructions
- Read each question carefully
- Select the best answer for each question
- You can retake the quiz as many times as you want
- Your progress will be shown at the top