Introduction
One of the powerful features of JavaScript is its asynchronous nature. And it is also one of the most difficult to master. But asynchronous JavaScript does not have to be hard.
When programming for the web a lot of functions have to behave asynchronous. If not, our programs would frequently get blocked waiting for some network transmission. Writing asynchronous code can however be quite tricky. This makes many people think that JavaScript is a difficult language. But it is really the problems we use JavaScript to solve that are tricky in the first place. And because JavaScript is so frequently used in an asynchronous manner, there are many tools that can help us.
Today we will have a look at a library that we use at Deductive Labs called Q. And how it can help make asynchronous programming easier.
Promises
Usually an asynchronous function returns a value when finished. What we can do instead is to return a Promise right away. A promise is a placeholder that will later become resolved with the value. Or rejected if something goes wrong.
Reading the value returned by an asynchronous function
The problem
Lets start by looking at the most basic problem. We have a function that asynchronously returns a value that we want to access.
https://gist.github.com/jakob-lundberg/19dc06062dffbfff4f7e
This fails as the value is not returned in time from the function.
The solution
To fix this we use the defer
function from Q.
https://gist.github.com/jakob-lundberg/8fe9be5e3086e83b1a5f
Now the function does not return the value, which is not available right away. Instead we use defer
to create a promise and that can be returned right away. When we get our value we can use this to resolve the promise.
The promise that is returned has a then
method. By using this method we can define the promise fulfillment handler. The fulfillment handler is the first argument of the then
method and is called when the promise is resolved. And it will be called with the value that was resolved.
Thus in the fulfillment handler we can access the value returned from the asynchronous function.
Chaining the asynchronous functions
The problem
In this case we want to pass the result from one asynchronous function into another asynchronous function. We first try to do it like this
https://gist.github.com/jakob-lundberg/9e1d8fe14cd619dd1d0a9cb829cf843a
But this will obviously not work. What we have to do first is wait for the first function to finish. And then send the value into the next one. And then wait for that one to finish. If we where to solve this using something like callbacks we would get nested function upon nested function. This makes it difficult to read and follow the code.
The solution
But fortunately it is possible to chain then
methods. This is done by having the first fulfillment handler return a promise from the next function in the chain.
https://gist.github.com/jakob-lundberg/9e2608096d491e0f09ac5de4fe274556
Here we can see the fulfillment handler of the first promise returning the promise from the addData
function. And on this promise we use the then
method to define the second fulfillment handler.
Combining asynchronous functions
The problem
Sometimes we have asynchronous functions, but we don’t want to chain them. We want to combine them. For example, we could be getting data from 2 separate sources. And then we want to add these data together.
https://gist.github.com/jakob-lundberg/3ee9a6e55fb09c235e72af26199397dc
We could solve this by again using chaining. But that would mean that we would wait for the first data before we fetch the second. And the whole point of doing this asynchronously is that we don’t have to wait, but can run multiple queries at the same time.
The solution
Luckily Q
has a method that can combine promises. The all
method takes an array of promises and returns a combined promise. This promise will not resolve until all the promises in the array have been resolved. And the resolved value will be an array with the individual promises values.
https://gist.github.com/jakob-lundberg/c40a9da133e0d5c4ea1819e294403eef
This makes it very easy to asynchronously gather lots of data and combine it once it is all collected.
Error handling
Sometimes functions are not able to return the value we are asking for. This is especially true when accessing data over the web. Therefore it is important that we are able to handle these situations correctly. And promises can help us with this as well. As mentioned earlier a promise can not only be resolved, it can also be rejected. To catch these rejected promises our then
method allows us to define rejection handlers. When a promise gets rejected it is possible to specify a reason. The rejection handler will then be called with this value.
https://gist.github.com/jakob-lundberg/0418164a8445d27d9a7a93f695967851
Chaining errors
With a long chain of promises it would get very verbose if we had to write a rejection handler for every promise. But fortunately a rejection will automatically propagate through the chain. Making it possible to just create one final rejection handler that takes care of any rejection that occurs in the chain.
https://gist.github.com/jakob-lundberg/d3c2a72c2c6279537ff135672f3cfc86
Combined errors
This is also possible when combining promises. If any of the individual promises gets rejected, then the combined promise will also get rejected.
https://gist.github.com/jakob-lundberg/ea93483a637293a30e5d5cf04845cabf
Conclusion
Due to tools like the Q
library, asynchronous JavaScript doesn’t have to be difficult. So take advantage of asynchronous functions as much as possible. As shown here it doesn’t have to be hard and it doesn’t have to make your code a mess.
Although we have been using Q
with Node.js here. It is also possible to use it in the browser. Just add
<script src="https://cdnjs.cloudflare.com/ajax/libs/q.js/2.0.3/q.min.js"></script>
and you are good to go.
Q
is quite flexible as is can work together with promises from for example jQuery. And learning how to use Q
is also useful for working with AngularJS, as the $q
service in AngularJS is inspired by Q
.
All examples can be found on Github