- Home
- /
- Tutorials
- /
- JavaScript Tutorial
- /
- JavaScript Promise Chaining
JavaScript Asynchronous
JavaScript Promise Chaining
Returning a value from
thenpasses it to the nextthenin the chain.
Each then returns a new promise, which is what makes chaining possible.
This is how promises replace deeply nested callbacks with a flat sequence.
Example
Example
javascript
Promise.resolve(2)
.then(function (n) {
return n * 5;
})
.then(function (n) {
console.log(n);
});The output is 10.
How Chaining Works
then returns a new promise.
Whatever you return from a handler becomes the value of that promise.
Returning a promise makes the chain wait for it.
Syntax
Syntax
javascript
promise
.then(stepOne)
.then(stepTwo)
.catch(handleError);A single catch at the end covers the whole chain.
Passing Values Along
Each step receives what the one before it returned.
Example
Example
javascript
Promise.resolve("a")
.then(function (value) {
return value + "b";
})
.then(function (value) {
return value + "c";
})
.then(function (value) {
console.log(value);
});The output is abc.
Waiting for Another Promise
If a handler returns a promise, the next step waits for it to settle.
This is what makes sequential asynchronous work readable.
Example
Example
javascript
function delay(value) {
return new Promise(function (resolve) {
setTimeout(function () {
resolve(value);
}, 10);
});
}
delay("one")
.then(function (a) {
return delay(a + " two");
})
.then(function (value) {
console.log(value);
});Compare this with the nested callback version: same work, flat shape.
The Missing return
Forgetting to return breaks the chain, and the next step gets undefined.
This is the single most common promise bug.
Example
Example
javascript
Promise.resolve(2)
.then(function (n) {
return n * 5;
})
.then(function (n) {
console.log(n === undefined ? "lost the value" : n);
});Without the return the output would be lost the value.
One catch for the Whole Chain
An error at any step skips straight to the catch.
Example
Example
javascript
Promise.resolve(1)
.then(function () {
throw new Error("step two failed");
})
.then(function () {
console.log("this is skipped");
})
.catch(function (error) {
console.log("caught: " + error.message);
});The output is caught: step two failed.
Carrying On After a catch
A chain recovers after a catch, so later steps still run.
Example
Example
javascript
Promise.reject("broken")
.catch(function () {
return "recovered";
})
.then(function (value) {
console.log(value);
});The output is recovered.
Complete Example
Complete Example
html
<!DOCTYPE html>
<html>
<head>
<title>JavaScript Promise Chaining</title>
</head>
<body>
<h1>Promise Chaining</h1>
<p id="out">Working...</p>
<script>
function delay(value, ms) {
return new Promise(function (resolve) {
setTimeout(function () {
resolve(value);
}, ms);
});
}
delay("Step 1", 500)
.then(function (value) {
document.getElementById("out").textContent = value;
return delay("Step 2", 500);
})
.then(function (value) {
document.getElementById("out").textContent = value + " — done";
})
.catch(function (error) {
document.getElementById("out").textContent = "Failed: " + error;
});
</script>
</body>
</html>Try It Yourself
Run the above example in the Try It Editor.
Try removing a return:
Delete the return before the second delay and watch the timing break.
Important Points
thenreturns a new promise, so calls can be chained.- What a handler returns becomes the next step's value.
- Returning a promise makes the chain wait for it.
- Forgetting
returnpassesundefinedon. - One
catchat the end covers every step.
Conclusion
Chaining is what turns nested callbacks into a readable sequence.
The rule to remember is simple: always return from a handler.
async and await make this flatter still.
