The Promise then() Function in JavaScript
Promises in JavaScript are an object
representation of an asynchronous computation. You can think of a promise as
a placeholder for a value that hasn't been computed yet. However, there's no way
to get a promise's value from the promise directly - you need to call the
then()
function to register a callback
that JavaScript will call when the value is computed.
// Create a promise that is immediately fulfilled with value 42.
const promise = Promise.resolve(42);
promise.then(value => {
value; // 42
});
The then()
Function's Parameters
The then()
function takes 2 callback function parameters:
onFulfilled()
: JavaScript will call this function if the underlying async operation succeeded.onRejected()
: JavaScript will call this function if the underlying async operation failed.
Recall that a promise is a state machine with 3 states:
- Pending The operation is in progress.
- Fulfilled The operation completed successfully.
- Rejected The operation experienced an error.
A promise always starts out in the pending state. If the promise transitions
to the fulfilled state, JavaScript calls the onFulfilled()
function. If you
call then()
on a promise that is already fulfilled, JavaScript will
immediately call onFulfilled()
.
const promise = new Promise(function executor(resolve, reject) {
// Fulfill the promise with value '42' after 100 ms.
setTimeout(() => resolve(42), 100);
});
promise.then(value => {
value; // 42
});
If the promise transitions to the rejected state, or if you call then()
on
a promise that is already rejected, JavaScript calls onRejected()
.
// Create a promise that is immediately rejected with an error object
const promise = Promise.reject(new Error('Oops!'));
promise.then(null, err => {
err.message; // 'Oops!'
});
Both onFulfilled()
and onRejected()
are optional. If onFulfilled()
is
null
like in the above example, JavaScript will do nothing if the promise is
fulfilled. If you call .then()
without an onRejected()
function and the
promise rejects, that will lead to an unhandled rejection error message.
then()
vs catch()
The Promise#catch()
function in JavaScript is a convenient shorthand for .then()
. Calling .catch(onRejected)
is
syntactic sugar for .then(null, onRejected)
.
// Create a promise that is immediately rejected with an error object
const promise = Promise.reject(new Error('Oops!'));
// Equivalent to `.then(null, onRejected)`
promise.catch(function onRejected() {
err.message; // 'Oops!'
});
Although .catch()
isn't complicated under the hood, the name catch
is
valuable because you can think of .catch()
as the promise analog to the
catch
part of try/catch
.
When you're scanning promise based code, seeing .catch()
makes it easy to
identify error handling.
Chaining
Promise chaining is one of the key reasons why promises are so useful. The
then()
function returns a promise p
, and if your onFulfilled()
function
returns a promise q
, p
will adopt
the state of q
.
// Create a promise that is immediately rejected with an error object
const promise = Promise.reject(new Error('Oops!'));
// Equivalent to `.then(null, onRejected)`
promise.catch(function onRejected() {
err.message; // 'Oops!'
});
Because of promise chaining, you don't need to catch errors in each individual
then()
. If you put catch()
at the end of your promise chain, any errors
in the promise chain will bypass the rest of the promise chain and go straight
to your catch()
error handler.
Promise.resolve(1).
then(() => Promise.resolve(2)).
then(() => Promise.reject(new Error('Oops!'))).
then(() => console.log('This will not print')).
catch(function errorHandler(err) {
err.message; // 'Oops!'
});