In Javascript, a promise is an object that represents the completion or failure of an asynchronous task.
You can think of promises as returned objects to which you can attach a callback (a fancy term for a function that gets called-back ~hehe~) as opposed to passing the callback into the function.
In my opinion, the best way to learn code is to read code so let's go over a simple example.
Imagine you have a function generateJazzMusicAsync()
which asynchronously generates jazz music. You want to pass a callback function that will play the jazz music once it is successfully generated or play rap music if the task failed to generate the jazz music.
In the olden days, you would pass a callback function
// imagine that generateJazzMusicAsync's implementation is provided elsewhere in this file
const preMadeRapMusic = generateRapMusic();
const succeededCallback = (input) => {
playMusic(input);
};
const failedCallback = (error) => {
playMusic(preMadeRapMusic);
};
generateJazzMusicAsync(succeededCallback, failedCallback);
But in the modern new world, we can use promises.
const preMadeRapMusic = generateRapMusic();
const succeededCallback = (input) => {
playMusic(input);
};
const failedCallback = (error) => {
playMusic(preMadeRapMusic);
};
generateJazzMusicAsync().then(succeededCallback).catch(failedCallback);
Some of the main advantageous of using promises include:
Unlike the old and tired passed-in callbacks, promises provides some garantees for us to base our logic on.
then()
, as above, will be called after the promise successfully resolves.catch()
block which if chained with another .then()
can provide you with a guarantee of how foreseen and unforeseen errors will be handled.then()
several times. Each callback is executed one after another, in the order in which they were inserted.Promises provide you with 4 functions to manage how you want your promises to be completed.
Promise.all()
takes in an array of promises as an input and then resolves to an array of the results from each promise. The returned promise will only resolve when ALL of the input promises resolve successfully, Promise.all()
will reject immediately if ANY promises were rejected and it will reject with the first error message occurred.
Promise.any()
takes in an array of promises as an input and it will resolve as soon as ANY of the promises succeed/resolve successfully. It will resolve to a single promise, the one that succeeded first. If none of the promises are fulfilled, then it will reject with AggregateError
.
Promise.race()
takes in an array of promises as an input and it will resolve as soon as ANY of the promises succeed OR reject. You can think of it as racing to see which promise will finish first whether it succeeds or not. This promise will either reject or resolve to whatever the first promise to finish rejected or resolved to.
Promise.allSettled()
takes in an array of promises as an input and it will resolve when ALL promises have settled, that is when all promises have rejected or resolved. It will return an array of statuses (full filled or rejected) as well as the accompanying error messages or resolved values.
In software, you will commonly want to execute tasks/functions back to back in a sequential fashion, to accomplish this with asynchronous tasks we will chain promises together.
doThing()
.then((res1) => {
return doSomethingElse();
})
.then((res2) => {
return doAnotherThing();
})
.catch((error) => {
console.error("Something went wrong while doing the thing");
});
Gotcha: Always return
results if you want the promise chain to make any sense. You need to return something from the first promise to act as input for the second promise.