One of the reasons I decided to write this post was because when I was learning async development a few years ago, it took longer than I’d have liked to learn from all the tutorials and posts out there which showed examples which were too basic. That’s why I’ll try to show some of my own recent usage of real-life code. I’ve also added some examples which can be ran quckly with a simple npm install on my Github repo for Async JS examples.
In the example above, the console logs will output the 1 and 4 first, and after around 1 second will run the code in the setTimeout functions, 2 and 3. In reality though, the browser will be doing more complex functions such as sending, receiving or storing data, which is where asynchronous code gets interesting.
When data is being sent/received from the application, we are required to do something when we receive the data (such as manipulating/displaying/saving the data), while the rest of the script can carry on running - this is the crooks of asynchronous programming. A common use case for me is displaying user data on a dashboard, for example:
The main bulk of this post after the intro will discuss new and fancy ways of writing asynchronous code, but in a lot of cases a simple callback as used here may be perfect to use.
The .done() or .success() functions are returned from the ajax function of jQuery, again allowing the rest of the script to run and allowing us to respond the the received data once we get it. It makes it easy to read the code and understand what’s going on. jQuery also offered the ability to use promises, which I won’t go into but they are not the same type of promise I’ll be discussing below.
then() to access the asynchronous data returned from a Promise, and
catch() to handle errors returned from a Promise, in a similar way to using callbacks mentioned above.
Promises could be used in third party libraries such as jQuery (as mentioned above) or libraries like Q which extend the Promise spec. There are some benefits for using the libraries as they add a few extra nice bits and will, by default, work on many more browsers than the native Promise feature, which is only an ES6 feature so not avaliable in IE for example. That discussion is for another day though! (or this SO post if you want to look now). I prefer to use the native Promise feature set and polyfill backward most of the time.
A simple Promise operation
Here’s a simple promise in action, using a
setTimeout() to simulate asynchronous behaviour to keep it simple. In a real-life situation this will be a server call which I’ll cover later:
At it’s very basic level the Promise syntax above looks nice and could be compared to callbacks in the way they look, but there are differences. Mainly that a Promise returns a call to a different function based on whether the process was successful using the resolve (success) or reject (failure).
One other main feature of Promises is being able to chain them together. Meaning that a function which returns a Promise can pass the returned data (resolved or rejected data) to another function (via
catch() respectively), and keep chaining returns that way. The above example can be extended to include multiple async operations, and chain events to run after the success or failure of each operation.
The below example simulates getting data from a server (server.js) which returns a Promise and chains multiple Promises together:
server.js (back end)
index.js (front end)
Anything returned from a Promise can be passed to a
then(), including another Promise. This is helpful with many situations, including things like the fetch API, which performs a similar set of tasks as above.
As shown above, it’s also important to use throw and catch to pass errors back up the chain to the final output.
One of the most powerful and useful features of Promises is being able to run asynchronous processes completely concurrently, and doing something when all processes are finished. This can be done with the
Here’s an example I have used in a previous project:
One important thing to remember when using Promise.all() is that if one Promise functon fails to resolve, the entire Promise.all() array will fail, so this needs to be factored in.
I was writing some back end code to get data from the Facebook Graph API and loop through them in an array map. Inside the map I wanted to construct my own object format and send that back to the new array (friends). This means though that for each person in the facebookResponse array from the Graph API there will be a new Promise object returned, so we end up with an array of many Promises. That’s where
Promise.all() comes in, which I think is one of the most powerful features of Promises for the day-to-day work I do. In the above example, the newly created array of objects is sent back to my app.
There is a bunch of additional bits to read up on when using Promises, so a few good places to start that is the Mozilla docs or Stack Overflow for some pretty creative usage ideas.
Async/await is a great part of the ES7 specification which is in most browsers now except IE (of course). It’s built on top of Promises and can be used alongside Promise. One of the main purposes of using async/await is to simplify the code and make it more readable, and it does this by making async code look synchronous.
A simple async/await operation
Here’s a simple example using Promises, and the same result achieved using async/await:
To me, the above code is much easier to read, and this is even more apparent when there’s a lot of chaining of different async functions going on.
This works because the
userData() returns a promise, and we can
await all Promises by adding the “await” keyword, which pauses the execution of the rest of the function until the Promise from the function is resolved. Here’s a snippet from the Mozilla docs:
When an async function is called, it returns a Promise. When the async function returns a value, the Promise will be resolved with the returned value. When the async function throws an exception or some value, the Promise will be rejected with the thrown value.
For clarity, here’s what the
userData() function in the above example could look like:
fetch() API returns a Promise by default and will therefore compliment an Async/await
getUserData() function perfectly, so a common practice is to have this in some sort of API section of your app (separation of concerns et al), and call the API functions elsewhere in your code. The fetch API is relatively new (2 or 3 years) and therefore not avaliable in IE at all, so be sure to include a polyfill or use an alternative solution such as Axios (which also returns Promises!).
Promises implements a chaining effect with the
catch() return functions because of the nature of how it processes asynchronous code, as shown in an example above. Async/await however is designed specifically so there doesn’t need to be long chains of
_saveUserData functions are both asynchronous and return a Promise, so we can use the above method to chain the events together. This is very useful in a lot of situations and looks easy on the eyes. Also again, remembering error catching with Async/await is crucial, as this will catch any errors from an asynchronous end point.
Chaining async functions is often needed in situations where you want to pass values from one function to the next, however there’s often a need to have asynchronous functions running in parallel, similar to the Promise concurrency example earlier where each instance of the array map of
facebookResponse runs at near enough the same time.
Here’s an example:
As mentioned earlier, async/await and Promises are not mutually exclusive, and in fact are often great when used together. The
Promise.all() can also be used in the situation to resolve when all functions have completed, and by awaiting the entire set of Promises in one go, the code can still be asynchronous and not have a load of
Again, remember that the Promise.all() will fail if one Promise inside the array fails.
Promises are great and kind of exciting to use, especially if you’ve previously been using callbakcs for years, and its even better when they are used alongside the async/await syntax to keep everything looking synchronous.
In part 2 of this post, I’ll be diving into Observables, which are different to Promises and arguably more complicated.
Hit me up if anyone has questions!