A promise is in one of three states: pending, fulfilled, or rejected. A promise has two 'fates': resolved or unresolved. A promise is settled if it's either fulfilled or rejected.
new Promise((resolve, reject) => {
someAsynchronousWork((result, error) => {
if (error) {
// This will call .catch
reject(error);
return;
}
// This will call .then
resolve(result);
});
});
Promises are chained with .catch
, .then
, and .finally
.
Each of these return its own promise, which can then subsequently be chained.
.then
does accept two arguments, onFulfilled
and onRejected
. It's called when a promise is resolved.
prom1.then(successCallback).catch(failureCallback); /* very similar to prom1.then(successCallback, failureCallback); */
A .catch
is essentially a .then
, without the onFulfilled
argument. It's called when a promise is rejected.
.catch
should always be used somewhere in the chain to handle errors. As this also returns a promise, it's possible to .then
a .catch
block. If you want to skip from one .catch
to another, avoiding any .then
s in between, you can re-throw an error.
Not having any .catch
chain will mean you have to deal with Unhandled promise rejection
errors.
.finally
will always run once the promise is settled, regardless of it being fulfilled or rejected. This is handy for cleanup and other tasks which you want to always run.
Promise.resolve
and Promise.reject
both exist to return a resolved and rejected promise with a value, respectively.
The next link in the chain will receive the return value of the previous, as each return their own promise with data inside. If you want to pass data from one .then
to another, make sure to return
the value explicitly, not just use it.
doSomething() .then(function (result) { // This has to be returned for `doThirdThing` to have `newResult` return doSomethingElse(result); }) .then(function (newResult) { return doThirdThing(newResult); }) .then(function (finalResult) { console.log(`Got the final result: ${finalResult}`); }) .catch(failureCallback);
It's generally better to use a .then
with a .catch
to handle resolved and rejected promises respectively, as it's easier to parse than two argument .then
s. It also allows you to have one area to catch all errors, regardless of which handler prompted them.
When using only a .then
, if the onFulfilled
function errors, it won't be caught by the onRejected
in the same .then
.
These are syntactic sugar for promises, and mainly exist to make code more readable. As soon as async
is added to a function, it will always return a Promise.
Note that using the async
keyword doesn't magically make things happen off the main thread. Async functions can still block the main thread if they're not actually async.
async function addNums(a, b) {
return a + b;
}
// Is the same as...
function addNums(a, b) {
return new Promise((resolve) => {
resolve(a + b);
});
}
// This code...
async function pingEndpoint(endpoint) {
const response = await fetch(endpoint);
return response.status;
}
// ...is equivalent to this:
function pingEndpoint(endpoint) {
return fetch(endpoint)
.then((response) => {
return response.status;
});
}
https://www.joshwcomeau.com/javascript/promises/ https://github.com/domenic/promises-unwrapping/blob/master/docs/states-and-fates.md
[[20210927102602-js-event-loop]]