问题
I have three async calls. One returns data, and two return images from S3 using firebase. I have one background DispatchQueue, and three dispatch groups. I need a way for them to execute synchronously, but they don't! I have tried everything, and .notify executes immediately, which is wrong.
The output of this is:
images done
all is done
group.notify is done
getImages() is done
What I want to understand is why is imagesdone executed before group.notify is done? I need to execute the first group, then imagesGroup, then avaGroup.
I essentially have three async calls, and the 2nd/3rd may be multiple async calls. How can I wait for them to complete, then execute subsequent calls?
func loadFriendPhotos() {
let backgroundQueue = DispatchQueue(label: "com.app.queue",
qos: .utility,
target: nil)
let group = DispatchGroup()
let imageGroup = DispatchGroup()
let avaGroup = DispatchGroup()
typealias tempAlias = (username:String, imageURL: String, pathUrl:String)
var tempAliasArray = [tempAlias]()
var imageArray = [UIImage]()
var avaImageArray = [UIImage]()
group.enter()
let workItem = DispatchWorkItem {
databaseRef.child("friendPhotos").child(globalUsername).observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.exists() {
let enumerator = snapshot.children
var childrenCount = snapshot.childrenCount
while let rest = enumerator.nextObject() as? FIRDataSnapshot {
let name = rest.childSnapshot(forPath: "username").value as! String
let downloadURL = rest.childSnapshot(forPath: "downloadURL").value as! String
let uid = rest.childSnapshot(forPath: "uid").value as! String
let pathURL = rest.childSnapshot(forPath: "pathURL").value as! String
let downloadURLRef = storage.reference(forURL: downloadURL)
let newTempAlias = tempAlias(name, downloadURL, pathURL)
tempAliasArray.append(newTempAlias)
}
group.leave()
}
})
}
func getAvaImages() {
for index in tempAliasArray{
avaGroup.enter()
let avaItem = DispatchWorkItem {
let avaURLRef = storage.reference(forURL: index.1)
avaURLRef.data(withMaxSize: 2 * 1024 * 1024) { (data,error) in
if (error == nil) {
print("success!")
let picData = UIImage(data:data!)
avaImageArray.append(picData!)
} else {
print(error?.localizedDescription)
}
print("we left getAvaImages()")
avaGroup.leave()
}
}
backgroundQueue.async(execute: avaItem)
}
}
func getImages() {
for index in tempAliasArray{
imageGroup.enter()
let imageItem = DispatchWorkItem {
let downloadURLRef = storage.reference(forURL: index.1)
downloadURLRef.data(withMaxSize: 2 * 1024 * 1024) { (data,error) in
if (error == nil) {
let picData = UIImage(data:data!)
imageArray.append(picData!)
} else {
print(error?.localizedDescription)
}
print("we left getImages()")
imageGroup.leave()
}
}
backgroundQueue.async(execute: imageItem)
}
}
backgroundQueue.sync(execute: workItem)
group.notify(queue: DispatchQueue.main, execute: {
print("group.notify is done")
getImages()
})
imageGroup.notify(queue: DispatchQueue.main, execute: {
print("images done")
getAvaImages()
})
avaGroup.notify(queue: DispatchQueue.main, execute: {
print("all is done")
})
}
回答1:
You're calling imageGroup.notify
at a point that there is nothing in imageGroup
, so it immediately notifies. Similarly, avaGroup
notifies immediately since it is empty at the point that you call it.
But I don't understand all the indirection and groups and three arrays. Why do you have to wait for all the images to download before you deal with each image? Why not just kick off the next step inside the original loop for just one element? Something like this:
var allTheResults = []
group.enter() // Enter once for the top-level
kickOffAsyncWithCompletion { resultList in
for item in resultList {
let resultThing = doSomeStuffToSetupNextLevel()
group.enter() // Enter once per thing we iterate over
kickOffNestedAsync(with: resultThing, completion: { result in
let finalResult = computeFinalResult(fromResult: result)
allTheResults.append(finalResult)
group.leave() // leave once per thing we iterate over
})
}
group.leave() // leave once for the top level
}
group.notify { doThing(withFinalResults: allTheResults) }
If you want to coordinate the endpoint of set of operations, you want all those operations to be working with the same group, not separate groups per operation.
来源:https://stackoverflow.com/questions/41493311/grand-central-dispatch-multiple-dispatchgroups