Resolving Promises
Asynchronicity in JavaScript
Yes, asynchronicity is a word. I verified it with a Google search, which also revealed its increasing importance in the JavaScript realm, as half of the top search results were related to JavaScript. And in JavaScript, asynchronous functions are heavily tied to Promises.
A Promiseis an object that represents the yet to be determined value of an asynchronous operation. A Promise has three potential states:
- unfulfilled/pending: the initial state, when a
Promisehas not been rejected but has yet to be fulfilled - fulfilled: the asynchronous operation has reached successful completion and the pending
Promisecan be fulfilled with a value - rejected: the asynchronous operation has failed and the pending
Promisecan return a reason for the error
MDN documentation supplies the following syntax for the Promise constructor:
new Promise( /* executor */ function(resolve, reject) { ... } );
The executor function is executed immediately, and usually initiates the asynchronous operation. It takes the resolve and reject functions as arguments because, upon completion of that asynchronous operation, it calls resolve or reject to change the state of the Promise. If the Promise is resolved, it is considered fulfilled and a value can be returned. If the Promise is rejected, an error can be thrown.
A promise chain can be created by chaining .then() to a returned Promise, allowing the return value of a fulfilled Promise to be passed into subsequent functions. In this chain, .catch() “catches” errors thrown by a rejected Promise. Using React and Redux, with thunk middleware, this pattern could be implemented within an action creator as follows:
export const fetchCats = () => {
return dispatch => {
return fetch(URL)
.then(response => response.json())
.then(function(cats) {
let mappedResponse = mapCats(cats);
return mappedResponse
})
.then(async (mappedResponse) => {
let kitties = await Promise.all(mappedResponse.map((cat) => {
return createCat(cat);
}))
// Always return results so they are available to the next element in the promise chain
return kitties
})
.then(function(kitties) {
dispatch(addCats(kitties))
})
.then(function() {
dispatch(clearGame())
})
.then(function() {
dispatch(playGame())
})
.catch(error => window.alert("Not enough kitties!"));
};
};
The example implements a couple other promise-related elements:
async: Introduced in ES 2017,asynccan be prepended to a function to return aPromiseand resolve or reject it as needed (without the explicitresolveorrejectof thePromiseconstructor)await: Also introduced in ES 2017,awaitcan be used in conjunction withasyncto pause the execution of theasyncfunction until thePromisethat followsawaitis resolved. Whileasynccan be used withoutawait, the latter can only be used within anasyncfunction.Promise.all: Is used with iterables and returns a single promise when all promises present in the iterable argument have been resolved. Upon any rejection, it rejects with the reason. The best part,Promise.allnot only returns aPromise, but preserves the order of the input, if the input is strictly ordered (i.e. an array).
There are a variety of ways to use promises in JavaScript. And the composition of one’s approach largely depends on 1) which version of JavaScript is used throughout the rest of the code-base (always good to maintain consistency), 2) which approach will provide the intended results with the greatest amount of clarity and readability (callbacks can get messy).
For the usage of async and await, MDN documentation provides a succinct determiner for usage:
The purpose of
async/awaitfunctions is to simplify the behavior of using promises synchronously and to perform some behavior on a group ofPromises. Just asPromisesare similar to structured callbacks,async/awaitis similar to combining generators and promises.
In the example above, there are other ways to work through an array of Promises but none would be as clean as prepending it with Promise.all. There is potential for enhancement in the realm of error/rejection handling as more .catch() statements could be added, to make debugging the promise chain easier. But, otherwise, it’s asynchronicity in action!