I have a function doEverything
that takes a completion block. It calls two other functions, doAlpha
and doBeta
which both have complet
Grand Central Dispatch (GCD) is a pretty good choice of what are you trying to do here, you can achieve this by using DispatchQueue and DispatchGroup, as follows:
Swift 3:
func doEverything(completion: @escaping () -> ()) {
let queue = DispatchQueue(label: "reverseDomain", attributes: .concurrent, target: .main)
let group = DispatchGroup()
group.enter()
queue.async (group: group) {
print("do alpha")
group.leave()
}
group.enter()
queue.async (group: group) {
print("do beta")
group.leave()
}
group.notify(queue: DispatchQueue.main) {
completion()
}
}
Or, you can implement it this way (which I find more readable):
func doEverything(completion: @escaping () -> ()) {
let queue = DispatchQueue(label: "reverseDomain", attributes: .concurrent, target: .main)
let group = DispatchGroup()
queue.async (group: group) {
print("do alpha")
}
queue.async (group: group) {
print("do beta")
}
group.notify(queue: DispatchQueue.main) {
completion()
}
}
Note that I removed the success
flag from the completion
closure.
At this case, "do beta" (the execution of the second queue.async
) won't be executed until "do alpha" (the execution of the first queue.async
) finished, and that's because queue
target is .main
. If you want to let both of queue.async
work concurrently, there is no need to create an extra queue, the same queue should does the work, by replacing:
let queue = DispatchQueue(label: "reverseDomain", attributes: .concurrent, target: .main)
with:
let queue = DispatchQueue(label: "reverseDomain", attributes: .concurrent)
Now, the system will control over how both of queue.async
tasks should work concurrently (and obviously, group.notify
will be executed after the tow of the tasks finish).
Hope this helped.
Ahmad F's answer is correct but, as my functions with callbacks return immediately (like most do) and the callbacks are executed later, I don't need to create a new queue. Here's the original code with changes to make it work.
func doEverything(completion: @escaping (success) -> ())) {
var alphaSuccess = false
var betaSuccess = false
let group = DispatchGroup()
group.enter()
doAlpha { success in
alphaSuccess = success
group.leave()
}
group.enter()
doBeta { success in
betaSuccess = success
group.leave()
}
group.notify(queue: DispatchQueue.main) {
completion(alphaSuccess && betaSuccess)
}
}
I didn't really want to force the completion call onto the main thread, but hey ¯\_(ツ)_/¯