• Skip to content

SwiftCraft

Master Your Craft

  • Resources
  • Podcast
  • About

How To: Perform an Action After Two Asynchronous Functions Finish

June 18, 2017

We all know how to run code after an asynchronous function is finished; simply use the completionHandler to perform final actions. But what about when we call two asynchronous functions and want to perform a task only after both finish?

In my particular case, I had to make two HTTP requests and update the UI after both had completed.

It would have looked clanky if half the UI updated after a single request finished, and the rest followed after a delay. There were other workarounds, but they were not elegant as they involved storing state. Surely there must be a better way!

Dispatch Groups

Grand Central Dispatch provides a simple solution to this problem. We can use DispatchGroup to bundle asynchronous tasks together. Once our grouped asynchronous tasks are completed, we will execute a block of code (in this example, we will update our UI).

Defining Two Asynchronous Functions

// Define asynchronous functions
func firstAsyncRequest(completionHandler: @escaping () -> Void) {
  // Do Stuff
  completionHandler()
}

func secondAsyncRequest(completionHandler: @escaping () -> Void) {
  // Do Stuff
  completionHandler()
}

Nothing complex here, we just define our asynchronous functions. In a more practical use-case, we would have our closures pass data back to us. But for the sake of simplicity we are skipping that.

Grouping Asynchronous Tasks

let group = DispatchGroup()

// Group our requests:
group.enter()
firstAsyncRequest {
  group.leave()
}

group.enter()
secondAsyncRequest {
  group.leave()
}

We call group.enter() before making each request, and group.leave() in the completionHandler once the request is finished. We end up with a group that has two tasks added to it, and we specify when each task leaves the group.

The magic happens once all tasks have exited the group.

Performing an Action After Grouped Tasks Finish

group.notify(queue: .main) {
  // Update UI
}

DispatchGroup notifies us once all the tasks we added into the group have left, and performs any block we pass into it. Think of this like as a completion handler, but for a group of tasks 🙂

Precautions

As with anything related to multithreading, there are a few pitfalls we should keep an eye out for.

  1. Make sure every call to enter() has a single corresponding call to leave(). If you have complex control flow, then call leave() in numerous places in order to ensure it gets called once.
  2. When our DispatchGroup calls notify, be careful to perform the task you pass into it on the correct queue. I needed to update the UI in my example, so I passed in the main queue.
  3. Add all your tasks to the group before calling notify(queue: execute:). That way you ensure it does not trigger before everything has been added to the group and finished executing.

Conclusion

Multithreading can be a pain, but Grand Central Dispatch offers a great deal of tools to simplify the process for developers. One pain-point has been handling multiple asynchronous calls once they are finished. We covered a great solution through the grouping asynchronous calls and utilizing notify(queue: execute:). This is a powerful tool found in Grand Central Dispatch, and should be found in every developer’s arsenal. And now it is in yours! 🙂

  • Click to share on Facebook (Opens in new window)
  • Click to share on Twitter (Opens in new window)
  • Click to share on LinkedIn (Opens in new window)
  • Click to share on Reddit (Opens in new window)

Related

Filed Under: Grand Central Dispatch, How To

About Eman Harout

Hello! I am Eman Harout, the founder of SwiftCraft, a place for Swift programmers to learn and improve their lot. I obsess over how code is architected and the tradeoffs associated with each approach. You can find me on Twitter @emanharout

Reader Interactions

Copyright © 2019 · Academy Pro on Genesis Framework · WordPress · Log in