Moving to promises and async/await from callbacks
Fetching the data in the web is an asynchronous task which is dreaded by many due to its a-synchronousness. Generally we have a tendency to write callbacks to handle asynchronous functions because it’s easier to write and understand. It might work when you have a few asynchronous functions to execute but as the code base starts growing and/or if there is a need for large number of asynchronous functions to be executed, you’ll have asynchronous functions passed as callbacks to asynchronous functions recursively. Lets consider this example
Here you have an array of super heroes and for each of them you have to fetch the data and after all the fetching is completed the completedFetchingData
function needs to be called. With asynchronous nature of the ajax requests how can you determine the point in time when all the data have been fetched? Let’s take a look at this gist where we have made a simple vanilla AJAX request to httpbin for all superheroes.
Will the above code function as intended?
Unfortunately for our need it wont always do. Here we have added a condition to execute the completedFetchingData
function when the data for the last superhero has been fetched. But it isn’t necessarily true that fetching the data for the last superhero completes executing at the end.
Due to the asynchronous nature of ajax requests and all ajax requests executing in parallel it is absolutely possible for the data of the superheroes to be fetched in any random order. Thus if the data for the last superhero gets fetched before other superheroes, the completedFetchingData
executes even before data for all have been fetched.
We could deal with the above problem by fetching the data for one superhero after fetching the data for the other in the callback recursively for all superheroes. This requires us to add the completedFetchingData
function to the callback for the ajax request for the last superhero. This would work but now you have made your ajax calls work more synchronously and your code looks ugly and difficult to test. Also you’d probably end up in callback hell.
Imagine having hundreds or thousands of superheroes in the roster. Is it scalable now… Probably not! This is where the need of promises comes in play.
Promises
If you aren’t new to JavaScript you must have heard of promises. It’s been there since the ES2015 standard. The concept of promise seems convoluted at first but once you get the hang of it it is pretty simple. Promise as the name suggests is like an assurance that a certain event will happen. A promise can be resolved or rejected. Until it is resolved or rejected it stays in pending state.
let promise = new Promise((resolve, reject) => {
if(/* asynchronous code execution is successful */) {
resolve(/* result */);
} else {
reject(/* error */);
}
});
resolve
and reject
are functions. resolve
needs to be called when the necessary asynchronous code gets successfully executed and reject
should be called only if any exception or problem occurs. Both resolve
and reject
accept arguments which are then passed to the .then
and .catch
functions respectively.
promise.then((result) => {
console.log(result);
},
(error) => {
console.log(error);
});
// you could handle the errors by passing it in .catch instead of .then as well
promise.catch((error) => { console.log(error); });
The other thing you need to know about is Promise.all
This is a nifty method for handling multiple promises.
Promise.all(/* arrayOfPromises */).then((values) => {
\\ values is array of resolved promise values from arrayOfPromises
},(error) => {
\\ if any of the promises in arrayOfPromises fails we reach here
});
It accepts an array of promises as an argument and returns a promise which gets resolved after all of the promises in the array passed gets resolved and gets rejected if any of the promises in the array gets rejected.
Here I’ve tried to integrate promises with callbacks without changing the structure of ajaxRequest
function. Promises for ajax requests for all superheroes have been created and collected in an array named superPromises
. Promise.all
method which waits for the fulfillment of all the promises passed to it and returns a single promise has also been integrated. Thus until all the promises in superPromises
are resolved, completedFetchingData
isn’t executed. Here all ajax requests can run asynchronously. The resolve and reject functions are carried out in the callback and hence only execute after the corresponding ajax request completes.
If an ajaxRequest
fails then the callback function is called with error as the parameter and the promise is rejected. If one of the promise in superPromises
fails then the promise returned by Promise.all
is rejected as well. Hence the error handling is also easily carried out.
Async / Await
Async / await
is a feature that was intended to be included in ES2015 but didn’t make a cut in that specification and was later released in ES2017. It’s a feature that lets us make unpredictable asynchronous functions work sequentially(synchronously).
const someAsyncFn = async (param) => {
const result = await someOtherAsyncFn(param);
return result;
}
Adding async
in front of a function lets us tag functions as asynchronous and these functions return a promise.await
is added in front of call of asynchronous functions i.e. the functions that return promises. It awaits for the completion of execution of the functions and thus the code looks more cleaner and more synchronous. Here it waits for someOtherAsyncFn
and assigns the value to result
before actually executing the next line i.e. returning the result
. await
can only be used inside async
functions.
Here I’ve ditched the callback and moved the promise to the return of the ajaxRequestwithPromise
function. I did this because this IMO is the proper way to use promises and also because async / await
feature doesn’t work with functions with callbacks as they are meant to return / receive promises not deal with callbacks.
I’ve made an async
function named request
and made the ajax requests in it for each of the superHeroes.
The await keyword added before the ajaxRequestWithPromise
function call makes the code execution await for the ajaxRequestWithPromise
to be resolved before iterating the loop. Thus the order in which the superHeroes
are passed is the order the data will be fetched. If you want all the superHeroes
data to be fetched asynchronously then you should switch to Promise.all
method which I mentioned above.
If an ajax request fails then the returned promise is rejected with an error and the enclosing async request
function’s promise is also rejected and can be handled by using the .then
or .catch
functions.
Thus I’ve illustrated in this article a few points why the use of promise
and async/await
is more preferable as opposed to the traditional callbacks in the long run. Do let me know in the comments if you strongly disagree with me and think that I’m wrong or if you think that this article helped you in any way. Here is the gist with all the pieces of code in one place. Also here are some links I loved to get started with promises and async functions.