Reputation: 1509
I'm trying to coordinate several completion handlers for each element in an array.
The code is essentially this:
var results = [String:Int]()
func requestData(for identifiers: [String])
{
identifiers.forEach
{ identifier in
service.request(identifier, completion: { (result) in
result[identifier] = result
})
}
// Execute after all the completion handlers finish
print(result)
}
So each element in the Array is sent through a service with a completion handler, and all the results are stored in an array. Once all of these handlers complete, I wish to execute some code.
I attempted to do this with DispatchQueue
var results = [String:Int]()
func requestData(for identifiers: [String])
{
let queue = DispatchQueue.init(label: "queue")
identifiers.forEach
{ identifier in
service.request(identifier, completion: { (result) in
queue.sync
{
result[identifier] = result
}
})
}
// Execute after all the completion handlers finish
queue.sync
{
print(result)
}
}
but the print call is still being executed first, with an empty Dictionary
Upvotes: 12
Views: 7797
Reputation: 3538
First, take note that your service.request(...)
is processed in asynchronous mode. Another problem is you want to finish all the service request in that loop.
My suggestion is create the function with completion handler and add a counter on each loop done. Your function will be similarly as below.
var results = [String:Int]()
func requestData(for identifiers: [String], callback:@escaping (Bool) -> Void)
{
var counter = 0
var maxItem = identifiers.count
identifiers.forEach
{ identifier in
service.request(identifier, completion: { (result) in
result[identifier] = result
counter += 1
if counter == maxItem {
callback(true) // update completion handler to say all loops request are done
}
// if not, continue the other request
})
}
}
This is how another part of your code will call the function and wait for callback
requestData(for identifiers:yourArrays) { (complete) in
if complete {
print(results)
}
}
Don't forget to manage if errors happened.
Upvotes: 0
Reputation: 942
If I understand what are you are trying to do correctly, you probably want to use a DispatchGroup
Here is an example:
let group = DispatchGroup()
var letters = ["a", "b", "c"]
for letter in letters {
group.enter()
Server.doSomething(completion: { [weak self] (result) in
print("Letter is: \(letter)")
group.leave()
})
}
group.notify(queue: .main) {
print("- done")
}
This will print something like:
b
c
a
// ^ in some order
- done
Upvotes: 41