The Key Idea: .then() Returns a New Promise
This is extremely important:
Every
.then()call returns a new promise.
Because of this:
- We can call another
.then()on the result. - Each
.then()receives the value returned from the previous one.
This allows us to create a chain of asynchronous operations.
Step 1: Basic Promise Chain
Let’s start with a simple example.
calculateSquare(1)
.then(value => {
console.log(value);
});
Output:
1
So far, nothing new.
Step 2: Adding Another .then()
Now let’s add another .then() right after the first one.
calculateSquare(1)
.then(value => {
console.log(value);
})
.then(value => {
console.log(value);
});
Output:
1 undefined
Why does this happen?
The second .then() receives the return value of the previous .then() callback.
But the first .then() does not return anything — and in JavaScript, that means:
return undefined;
So the second .then() receives undefined.
Returning a Value from .then()
Now let’s return something explicitly.
calculateSquare(1)
.then(value => {
console.log(value);
return 25;
})
.then(value => {
console.log(value);
});
Output:
1 25
Now the second .then() receives the value returned by the first one.
Throwing an Error Inside .then()
What if something goes wrong?
We can throw an error inside a .then() callback.
calculateSquare(1)
.then(value => {
console.log(value);
throw new Error("Something went wrong");
})
.then(value => {
console.log(value);
})
.catch(error => {
console.log(error.message);
});
Output:
1 Something went wrong
What happened?
- Throwing an error automatically rejects the promise
- Control jumps to the nearest
.catch() - The chain stops executing normal
.then()handlers
Returning Another Promise from .then()
Now comes the most powerful part.
Instead of returning a value, we can return another promise.
calculateSquare(1)
.then(value => {
console.log(value);
return calculateSquare(2);
})
.then(value => {
console.log(value);
});
Output:
1 4
Why does this work?
When you return a promise from .then():
- JavaScript waits for that promise to resolve
- The resolved value is passed to the next
.then()
This allows clean and readable asynchronous chains.
Handling Rejected Promises in a Chain
Now let’s see what happens when an error occurs.
calculateSquare(1)
.then(value => {
console.log(value);
return calculateSquare("wrong input");
})
.then(value => {
console.log(value);
})
.catch(error => {
console.log(error);
});
Output:
1 Argument of type number is expected
Explanation:
- The second call rejects the promise
- The rejection skips all remaining
.then()calls - The
.catch()handler receives the error
Key Concept: Promise Chaining Flow
.then() → returns value → next then gets value .then() → returns promise → waits for it .then() → throws error → jumps to catch()
