Moving to promises and async/await from callbacks

Aashish Manandhar
Devnetwork
Published in
6 min readJan 8, 2018

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.

Notice that the order of the data fetched is different from superHeroes array.

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.

The order of data fetched is same as the order in superHeroes array.

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.

--

--

Aashish Manandhar
Devnetwork

Just another guy who wants to make world a better place!