One Promise to Rule them .all…
one Promise to .find(them). One Promise to bring them all and in the darkness .bind(them)
That piece of syntax is Promise.all.
First, a bit on Promises
Promises encapsulate asynchronous code and create an easy way of starting and resolving streams of code out of direct sequence. Whereas event listeners are instantiated and then respond to external events asynchronously — which can create issues where an event that occurs before the listener is instantiated goes overlooked — a Promise begins and ends all within defined code blocks, and resolves once.
By create a Promise, we can get any kind of time consuming process started and then telegraph the outcomes given whatever the result turns out to be once that process resolves.
Once the promise is declared, we can use .then syntax to string it with actions that must take place after the result comes back.
For example, if we’re calling an API for a JSON result in our Promise and we want to then populate that data into state, we would have to do this after we know that the data has come back.
In the above example, we’re using ‘fetch’ to make a request to a given url. ‘fetch’ returns a Promise that resolves with the response from the request. We chain that response through two ‘.then’ statements — error catching along the way — to turn the response into JSON, and then populate it into state (I’m just using ‘this.setState’ here for the purpose of the example).
Where this gets tricky is if we needed to use the data we had populated for something:
This will break, because the response in the asynchronous chain may not have returned yet when the last line of code (a sync task) is run.
We could very easily just stick this into another ‘.then’, but you can imagine how burdensome and inefficient long chains of ‘.then’ statements would get after waiting for the initial response to resolve. Not quite Hadouken-bad, but still pretty unweildy.
Not only this, but what would happen if that last line of code relied not just on the resolution of the first Promise but on that of a slew of others, all running asynchronously?
I think we can all agree that this is way out of control. The simple fix of chaining the final sync task in a ‘.then’ is long gone, and we have no easy way of knowing which Promise will resolve last so that we can then trigger code that relies on the complete resolution of all of the Promises.
How can we subjugate and keep track of all of our Promises?
Dark Magic, of course.
Oh my god, if this works…
Nope, this still has the potential to break. But we’re almost there.
The way that Promise.all works, if even one of the promises in its array argument throws an error, the ‘.then’ statement won’t run and the ‘.catch’ will. This is not ideal. Imagine if Sauron’s power could be drained by just destroying a single of the 19 Rings of Power that he created for the other races of Middle Earth. It would make for a much less compelling story, at the very least.
If you’re fine with hating yourself forever, you can employ the cheap hack of writing the same processes to run in both ‘.then’ and ‘.catch’, which will cause that code to run no matter which is called.
But as always, there is a better way:
By mapping through the promises and defining the ‘.catch’es as undefined, we are able to ensure that this will always hit the ‘.then’ statement after all promises in the ‘promises’ array have resolved! (it should be noted that this adds n complexity to the solution, where n=promises.length; nothing to worry about if promises.length is of a small order of magnitude)
Now go build something! Just be careful not to become this guy: