How to Chain Asynchronous Methods in JavaScript
If you’ve worked with JavaScript you’ve probably done a thing or two with asynchronous operations. This article will show you how to sequence or chain asynchronous operations so the next one begins after the first is completed. We can use an iterator, loops, or promises to do it in a clean way and avoid doing something like below.
Here’s why the code above can be problematic:
- Poor readability — as it stands you can see that it is already a bit difficult to read and it will only get worse the more asynchronous operations you add to it and the more code you need to write in between the calls.
- More susceptible to bugs — all code is susceptible to bugs. This snippet of code even more so because we are nesting so much of it. This means that we not only have to be extra careful about variable names but also that we are writing inside of the correct code segment. You can see that I named the responses and errors differently to have to account for this.
- Not reusable —sure we might be able to “reuse” this entire snippet, but what if I only want to use 2 methods instead of 3 next time? Then it becomes unusable.
So how can we improve this? Let’s start by creating our own JavaScript iterator to do this for us in a class. First, define an array of methods that will be used.
We have three AJAX methods we want to call in order. ajax1(seconds)
, ajax2()
, and ajax3()
. Please note that these do not need to be AJAX methods, it can be any asynchronous method.
The route test.ajaxCalls
calls a simple PHP function that sleeps for a period of time.
Now let’s define our method iterator class.
We will save the iterator state inside of the this.iterator
variable. We will be referencing this iterator every time the next()
function is called. When you create the iterator, implement the iterator protocol by returning an object with value and done properties.
In our case value
is the next function of our methods
array. Done
is a boolean that becomes true when there are no more items to iterate over.
Let’s create the methods that will start our iterator and then keep it going.
The execute()
function gets our iterator started, all we are doing here is calling iterate()
with the first iteration as an argument.
The iterate(iteration)
function takes a specific iteration as an argument. If we are not through consuming our iterator we will call the method (represented by res,iteration.value
) and then we will call iterate(iteration)
again so that we consume our iterator via recursion. Additionally, we can execute some generic action here before moving onto the next iteration. Now, to put our class into action we just need a few lines.
Based on this example we can expect to see the following results in the console.
[5 SECONDS PASS]
ajax1Response: {...}
iteratorResponse: {...}
[2 SECONDS PASS]
ajax2Response: {...}
iteratorResponse: {...}
[3 SECONDS PASS]
ajax3Response: {...}
iteratorResponse: {...}
Iterating Over Synchronous and Asynchronous Methods
What if we want to iterate over async and non-async functions? In this case let’s change our methods
array to contain a list of objects which will have async and func properties. The async property will be a boolean that tells us whether the function is asynchronous and the func property is the function that we want to execute.
The new methods
array.
You will need to update the iterate(iteration)
method inside of methodIterator
class to account for these changes. Check whether or not the function is asynchronous first, and then call iteration.func()
with or without .then()
as necessary. Now we can expect the following results in the console.
[5 SECONDS PASS]
ajax1Response: {...}
iteratorResponse: {...}
Not Asynchronous.
[2 SECONDS PASS]
ajax2Response: {...}
iteratorResponse: {...}
[3 SECONDS PASS]
ajax3Response: {...}
iteratorResponse: {...}
Notice how not_async
was called immediately, and 2 seconds later, ajax2()
returned a response.
Come on, there has to be an easier way to do this!
Here are two other ways to do this without using the iterator protocol. They are not necessarily easier. It also isn’t necessary to create your own class, though it might be a good idea depending on your needs.
The first is a for loop that uses await.
This will yield the following in the console.
[5 SECONDS PASS]
ajax1Response: {...}
Not Asynchronous
[2 SECONDS PASS]
ajax2Response: {...}
[3 SECONDS PASS]
ajax3Response: {...}
We can also use promises and promise chains.
Which will yield the following in the console.
[5 SECONDS PASS]
ajax1Response: {...}
Not Asynchronous
[2 SECONDS PASS]
ajax2Response: {...}
[3 SECONDS PASS]
ajax3Response: {...}
Methods complete.
There are many ways to iterate through asynchronous methods and there are also many use cases for it. If you have any questions, want to point something out, or share an alternative — comment below!