Promises are currently the best tool we have for asynchronous programming and they appear to be our best hope for the forseeable future, even if they’ll be hiding behind generators or async functions. For now, we’ll need to use promises directly, so we should learn some good techniques for using them right now, especially when dealing with asynchronous operations on collections, whether they happen in parallel or sequentially.
In the code,
asyncOperation just represents a function that takes a single number parameter, performs an asynchronous operation according to that number, and returns a promise, while
// ... represents whatever code is specific to your application that operates on the values returned from
Each of the functions I create, it will run the
asyncOperation on all of the values in the
values array and return a promise that resolves to an array of the values that
First we’ll take a look at parallel operations. This refers to getting multiple asynchronous operations queued up and running at the same time. By running them in parallel, you can significantly increase your performance. Sadly, this isn’t always possible. You may be required to run the operations in sequential order, which what we’ll be talking about in the next section.
Anyway, we’ll first look at running the asynchronous operations in parallel, but then performing synchronous operations on them in a specific order after all of the asynchronous operations have finished. This gives you a performance boost from the parallel operations, but then brings everything back together to do things in the right order when you need to.
map to get all of our asynchronous operations fired off right away, but then use
Promise.all to wait for them all to finish, and then we just run a loop over the new values and do whatever operations we need to do in the original order.
Sometimes, the order that our synchronous operations run in don’t matter. In this case, we can run each of our synchronous operations immediately after their respective asynchronous operations have finished.
For this, we use
map again, but instead of waiting for all of the operations to finish, we provide our own callback to
map and do more inside of it. Inside we invoke our asynchronous function and then call
then on it immediately to set up our synchronous operation to run immediately after the asynchronous one has finished.
Let’s take a look at some patterns for sequential asynchronous operations. In this case, the first asynchronous operation should finish before moving on to the next asynchronous operation. I have two solutions for doing this, one uses
forEach and one uses
reduce. They are quite similar, but the version with
forEach needs to store a reference to the promise chain, whereas the version with
reduce passes it through as the memo. Essentially, the version with
forEach is just more explicit and verbose, but they both accomplish the same thing.
In each version we just chain each asynchronous operation off of the previous one. It’s annoying that we need to create a “blank” promise that is simply used to start the chain, but it’s a necessary evil. Also, we need to explicitly assign values to the
newValues array (assuming you want to return those), which is another necessary evil, though maybe not quite as evil. I personally think the version with
forEach is slightly easier to read thanks to its explicit nature, but it’s a stylistic choice and
reduce works perfectly for this situation.
I used to think promises weren’t very straight-forward and even had a hard time finding a reason to use them over standard callbacks, but the more I need them, the more useful I find them to be, but I also find them to be more complicated with numerous ways they can be used, as shown above. Understanding your options and keeping a list of patterns you can follow greatly helps when the time comes to use them. If you don’t already have these patterns embedded in your brain, you may want to save them somewhere so you have them handy when you need them.
Well, that’s all for today. God bless! Happy Coding!