Dispatch groups: A way of controlling parallel asynchronous responses
Imagine the following scenario: You have a screen where you are supposed to display details from a list of cards. Everything you receive as an input is an array of card ids which you can use them to fetch details from a back-end call. You may iterate each of them and as you get their details object, you just append the new result to a details array. When you have all the data you need, just pass everything to the presentation layer and you have a totally filled screen. Is that trivial right? No, it's not! This is much more complex than you might be thinking..
Remember that when you are making an API call, you are switching threads passing some control to the background while the main flow deals with the UI and some essential tasks. With that, you can't know at all when you are supposed to retrieve your result. Also, you have multiple tasks to be done, and your natural way of thinking is that…Ok! I got some data, I am ready to display it. Again, no, you are not.
Let's imagine that we are working on a clean architecture where our Interactor layer knows all the card ids and requests the details from the Service layer. When we have data to present, we call the Presenter layer.
This picture illustrates exactly what's happening:
What is the real problem in this image? When you fetch the details from the first API call(background thread), you are already passing the success data to the Presenter to show it and you don't have a complete result. Of course you can replace the data you are presenting gradually to the updated version, but since you are in the main thread when presenting the UI, that would cost some computational time and it would not be a good practice.. So, you need to know when does the array stop iterating, and there is a default way of thinking which is far from being the optimal solution..But besides, we are illustrating and explaining why it's not correct.
Default solution(avoid this)
As a programmer, you may be thinking on how to detect the end of an array for finally passing the control to the Presenter. Take a look at this code:
This solution as you noticed just takes the newly fetched information from a card id and appends it to the details array called
allCards . In each iteration it just checks if the index is equal to the last valid index of the array. That's not the optimal solution because you are checking manually the index and comparing to the last one, when you have a much simpler solution to schedule a list of tasks.
Now you need some kind of mechanism to detect when a queue of asynchronous tasks has ended. This is exactly where dispatch groups can solve our problem.
Let's get back to our
fetch function and replace the code with a simpler solution:
If you take a look, what's happening now is that we created a new instance of the type
DispatchGroup and notifying that a new event started at the queue for each card id we iterate. When our task is done, we emit a new event to the dispatch group saying that it concluded. When we are sure that all the dispatch events finished, we trigger a callback in the main queue to pass everything down.
Now our method is complete: We enter in a new event in the group when we iterate through a new card id. We pass the control to a background thread when requesting data from the API, and then, when we have a result from the completion, we can leave the group because our job is done. We also declared in the main flow a listener to the dispatch group notifying when our group is empty of tasks. This way we can make sure we are passing the control down only when there are no more card ids to research:
I shall mention that this structure for our calls is not the best one, because in a straight banking app, the back-end should be capable of returning all the details from the card list all at once, without needing any identifier.
In this article we presented a new solution for controlling multiple parallel tasks whose results rely on each other for delivering a bigger merged one. With that, you can detect if all the job is done.