Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

dispatch group: for-loop *and* async calls within the for-loop

I understand how to use a dispatch group in a simple for-loop. I, however, have a more complicated for-loop with more asynchronous calls within it. I want all the asynchronous calls to complete before executing the group completion code. I have tried to apply the principles in this answer to no avail- Can I use dispatch group not in a loop?. Here is my code, based off of the techniques I observed in that link:

let group = DispatchGroup()

for ref in self.notifsRefList {
    group.enter()
    self.db.fetch(withRecordID: ref.recordID) { notifRecord, notifErr in
        print("async call")
        if notifErr == nil {

            // do stuff

            if let ref = notifRecord?.object(forKey: POST) as! CKReference? {
                group.enter()
                self.db.fetch(withRecordID: ref.recordID) { postRecord, err in
                    print("async call")
                    if err == nil {
                        // do stuff
                        group.leave()
                    }
                    else {
                        print("\(err)")
                        group.leave()
                    }
                }
            }
            if let ref = notifRecord?.object(forKey: USER_NOTIF) as! CKReference? {
                self.db.fetch(withRecordID: ref.recordID) { userRecord, err2 in
                    group.enter()
                    print("async call")
                    if err2 == nil {
                        // do stuff
                        group.leave()
                    }
                    else {
                        print("\(err2)")
                        group.leave()
                    }
                }
            }
            if let ref = notifRecord?.object(forKey: LIBRARY_ITEM) as! CKReference? {
                self.db.fetch(withRecordID: ref.recordID) { libRecord, err3 in
                    group.enter()
                    print("async call")
                    if err3 == nil {
                        // do stuff                        
                        group.leave()
                    }
                    else {
                        print("\(err3)")
                        group.leave()
                    }
                }
            }

            group.leave()

        }
        else {
            print("\(notifErr)")
            group.leave()
        }
    }

}


group.notify(queue: .main, execute: { // executed after all async calls in for loop finish
    print("done with all async calls")
    // do stuff
})

From the print statements I included, I know that my asynchronous calls are incorrect: sometimes "done with all async calls" prints before all instances of "async call." Any help in how to get this dispatch group working properly would be much appreciated. Thanks!

like image 552
mlecoz Avatar asked Apr 05 '17 23:04

mlecoz


1 Answers

The problem is the 2nd and 3rd inner async calls. You are calling group.enter() inside the completion blocks instead of before the async calls.

There's also no need for so many calls to leave.

You need to move the two as follows:

let group = DispatchGroup()

for ref in self.notifsRefList {
    group.enter()
    self.db.fetch(withRecordID: ref.recordID) { notifRecord, notifErr in
        print("async call")
        if notifErr == nil {

            // do stuff

            if let ref = notifRecord?.object(forKey: POST) as! CKReference? {
                group.enter()
                self.db.fetch(withRecordID: ref.recordID) { postRecord, err in
                    print("async call")
                    if err == nil {
                        // do stuff
                    }
                    else {
                        print("\(err)")
                    }
                    group.leave()
                }
            }
            if let ref = notifRecord?.object(forKey: USER_NOTIF) as! CKReference? {
                group.enter()
                self.db.fetch(withRecordID: ref.recordID) { userRecord, err2 in
                    print("async call")
                    if err2 == nil {
                        // do stuff
                    }
                    else {
                        print("\(err2)")
                    }
                    group.leave()
                }
            }
            if let ref = notifRecord?.object(forKey: LIBRARY_ITEM) as! CKReference? {
                group.enter()
                self.db.fetch(withRecordID: ref.recordID) { libRecord, err3 in
                    print("async call")
                    if err3 == nil {
                        // do stuff                        
                    }
                    else {
                        print("\(err3)")
                    }
                    group.leave()
                }
            }
        }
        else {
            print("\(notifErr)")
        }
        group.leave()
    }
}

group.notify(queue: .main, execute: { // executed after all async calls in for loop finish
    print("done with all async calls")
    // do stuff
})
like image 189
rmaddy Avatar answered Sep 30 '22 17:09

rmaddy