Running multiple async tasks and get a notification after completion of all tasks

Viewed 494

I want to run a couple of asynchronous tasks and get a notification after all of them are finished. The tasks should be run concurrently. Is what i'm doing correct? because it calls the notification immediately and does not wait for task to finish:

let dispatchGroup = DispatchGroup()
let queue = DispatchQueue(label: "com.name.group", attributes: .concurrent)

queue.async(group: dispatchGroup) {
    self.asyncFunc1() { (data) in
        // returns something
    }
}

queue.async(group: dispatchGroup) {
    self.asyncFunc2() { (data) in
        // returns something
    }
}

queue.async(group: dispatchGroup) {
    self.asyncFunc3() { (data) in
        // returns something
    }
}

dispatchGroup.notify(queue: queue) {
    DispatchQueue.main.async {
        // Update the UI
    }
}

Should not dispatchGroup.notify() get fired after all three calls are done?

1 Answers

Dispatch groups are the right tool for this, but you are using them wrong. The group is left as soon as the block passed to queue.async is finished. Instead you also could write something like this:

group.enter()
queue.async {
   // Stuff
   group.leave()
}

Once a leave call happened for each enter call the notify function calls its block.

And there is your problem - the leave call happen after your async operations have been started, not finished. For your use case the async(group:) method does not work, you need to manually call enter before you call your async functions and call leave in the completion handler:

group.enter()
asyncFunc1() { data in
  // ...
  group.leave()
}

Depending on what you do in your callback blocks you might want to use defer to call leave so it happens in any case.

By the way, if all you need to do in your notify handler needs to run on the main queue you also can pass this directly to notify. Dispatching to another queue just to go back to main is unnecessary and inefficient. You can use any queue - it doesn’t have to be the one you scheduled your work on. The whole point of dispatch groups is to synchronize multiple queues.

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