Dispatch.notify 在任务完成之前被调用?

Dispatch.notify being called before task has completed?

我正在尝试从 Firestore 下载数据,将其附加到一个数组,然后在下载所有数据后将其附加到另一个数组。我尝试使用完成处理程序,但它 returns 即使数据尚未全部下载。所以我尝试使用 DispatchGroup 但是 dispatch.notify 在我的任务完成之前被调用。

这是我的代码。我在最后一个 else 括号内调用 dispatch.begin 否则 leave 和 enter 出于某种原因不平衡?

如何等到所有数据都下载完毕,然后在 dispatch.notify 中调用完成?

func SameUniRelatedCourses(completion: @escaping (_ success: Bool) -> Void) {
        DispatchQueue.main.async {
            self.spinner.startAnimating()
        }
        service.loadUniversityAndCourse { (uni, course) in
        let related = RelatedCourses()
        let relatedCourseArray = related.getRelatedCourses(userCourse: course)//.prefix(4)
        for Course in relatedCourseArray {
            let UniRef = Firestore.firestore().collection("User-Courses").document(Course)
                UniRef.getDocument { (snapshot, error) in
            if let error = error{
                print(error.localizedDescription)
            }
            else {
                //append their data to an array
                guard let data = snapshot?.data() else {return}
                let stringArray = Array(data.keys)
                for user in stringArray {
                    let usersRef = Firestore.firestore().collection("users").document(user)
                    usersRef.getDocument { (snapshot, error) in
                if let error = error {
                    print(error.localizedDescription)
                }
                else {
                    let data = snapshot?.data()
                    //print("raw data", data?["username"]!)
                    if let dictionary = data as [String:AnyObject]? {
                    let Info = UserInfo(dictionary: dictionary)
                        if Info.University != uni {
                            //print("Not in their uni",Info.username!)
                        }
                        else if self.sameUniSameCourse.contains(where: { [=11=].uid == Info.uid }) {
                           //print("already in sameunisamecourse",Info.username!)
                        }
                        else {
                            print("entering")
                            self.dispatchGroup.enter()
                            print("1", Info.username!)
                           self.sameUniRelatedCourses.append(Info)
                            self.sameUniRelatedCourses.sort { (time1, time2) -> Bool in
                                return time2.Created!.seconds/1000 > time1.Created!.seconds/1000
                            }
                            print("leaving")
                            self.dispatchGroup.leave()
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
        self.dispatchGroup.notify(queue: .main) {
            print("done")
            }
    }

第二个函数

func sameUniversity(completion: @escaping (_ success: Bool) -> Void) {
        DispatchQueue.main.async {
            self.spinner.startAnimating()
        }
        
        self.dispatchGroup.enter()
        service.loadUniversityAndCourse { (uni, course) in
            defer{ self.dispatchGroup.leave() }
        let UniRef = Firestore.firestore().collection("User-Universities").document(uni)
        UniRef.getDocument { (snapshot, error) in
        if let error = error{
            print(error.localizedDescription)
        }
        else {
            //append their data to an array
            guard let data = snapshot?.data() else {return}
            let stringArray = Array(data.keys)
            for user in stringArray {
                let usersRef = Firestore.firestore().collection("users").document(user)
            usersRef.getDocument { (snapshot, error) in
            if let error = error {
                print(error.localizedDescription)
            }
            else {
                let data = snapshot?.data()
                //print("raw data", data?["username"])
                if let dictionary = data as [String:AnyObject]? {
                let Info = UserInfo(dictionary: dictionary)
                    if self.sameUniSameCourse.contains(where: { [=12=].uid == Info.uid }) || Info.uid == Auth.auth().currentUser?.uid || self.sameUniRelatedCourses.contains(where: { [=12=].uid == Info.uid }) {
                        //print("already in master array", Info.username!)
                    }
                    else {
                        print("2", Info.username!)
                        self.sameUni.append(Info)
                        self.sameUni.sort { (time1, time2) -> Bool in
                            return time1.Created!.seconds/1000 > time2.Created!.seconds/1000
                                }
                            }
                        }
                    }
                }
            }
        }
    }
  }

        self.dispatchGroup.notify(queue: .main) {
        print("done")
        //call Completoion here
        self.masterArray.append(contentsOf: self.sameUni)
        self.spinner.stopAnimating()
        self.tableView.reloadData()
        completion(true)
        }

}

你需要像这样使用Dispatch Group。在执行异步任务之前进入并在完成闭包内调用leave

 func SameUniRelatedCourses(completion: @escaping (_ success: Bool) -> Void) {
    DispatchQueue.main.async {
        self.spinner.startAnimating()
    }
    
    self.dispatchGroup.enter()
    service.loadUniversityAndCourse { (uni, course) in
       defer{ self.dispatchGroup.leave() }
        
        
    let related = RelatedCourses()
    let relatedCourseArray = related.getRelatedCourses(userCourse: course)//.prefix(4)
    for Course in relatedCourseArray {
        self.dispatchGroup.enter()
        let UniRef = Firestore.firestore().collection("User-Courses").document(Course)
            UniRef.getDocument { (snapshot, error) in
                defer{ self.dispatchGroup.leave() }
        if let error = error{
            print(error.localizedDescription)
        }
        else {
            //append their data to an array
            guard let data = snapshot?.data() else {return}
            let stringArray = Array(data.keys)
            for user in stringArray {
                self.dispatchGroup.enter()
                let usersRef = Firestore.firestore().collection("users").document(user)
                usersRef.getDocument { (snapshot, error) in
                    defer{ self.dispatchGroup.leave() }
            if let error = error {
                print(error.localizedDescription)
            }
            else {
                let data = snapshot?.data()
                //print("raw data", data?["username"]!)
                if let dictionary = data as [String:AnyObject]? {
                let Info = UserInfo(dictionary: dictionary)
                    if Info.University != uni {
                        //print("Not in their uni",Info.username!)
                    }
                    else if self.sameUniSameCourse.contains(where: { [=10=].uid == Info.uid }) {
                       //print("already in sameunisamecourse",Info.username!)
                    }
                    else {
                        print("entering")
                        
                        print("1", Info.username!)
                       self.sameUniRelatedCourses.append(Info)
                        self.sameUniRelatedCourses.sort { (time1, time2) -> Bool in
                            return time2.Created!.seconds/1000 > time1.Created!.seconds/1000
                        }
                        print("leaving")
                       
                                }
                            }
                        }
                    
                     
                    
                    }
                }
            }
                
        }
    }
        
}
    self.dispatchGroup.notify(queue: .main) {
        print("done")
        //call Completoion here
        }
}

第二个函数

 func sameUniversity(completion: @escaping (_ success: Bool) -> Void) {
            DispatchQueue.main.async {
                self.spinner.startAnimating()
            }
            
            self.dispatchGroup.enter()
            service.loadUniversityAndCourse { (uni, course) in
                defer{ self.dispatchGroup.leave() }
            let UniRef = Firestore.firestore().collection("User-Universities").document(uni)
                
                self.dispatchGroup.enter()
            UniRef.getDocument { (snapshot, error) in
                defer{ self.dispatchGroup.leave() }
            if let error = error{
                print(error.localizedDescription)
            }
            else {
                //append their data to an array
                guard let data = snapshot?.data() else {return}
                let stringArray = Array(data.keys)
                for user in stringArray {
                    self.dispatchGroup.enter()
                    let usersRef = Firestore.firestore().collection("users").document(user)
                usersRef.getDocument { (snapshot, error) in
                    defer{ self.dispatchGroup.leave() }
                if let error = error {
                    print(error.localizedDescription)
                }
                else {
                    let data = snapshot?.data()
                    //print("raw data", data?["username"])
                    if let dictionary = data as [String:AnyObject]? {
                    let Info = UserInfo(dictionary: dictionary)
                        if self.sameUniSameCourse.contains(where: { [=11=].uid == Info.uid }) || Info.uid == Auth.auth().currentUser?.uid || self.sameUniRelatedCourses.contains(where: { [=11=].uid == Info.uid }) {
                            //print("already in master array", Info.username!)
                        }
                        else {
                            print("2", Info.username!)
                            self.sameUni.append(Info)
                            self.sameUni.sort { (time1, time2) -> Bool in
                                return time1.Created!.seconds/1000 > time2.Created!.seconds/1000
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
      }

            self.dispatchGroup.notify(queue: .main) {
            print("done")
            //call Completoion here
            self.masterArray.append(contentsOf: self.sameUni)
            self.spinner.stopAnimating()
            self.tableView.reloadData()
            completion(true)
            }

    }

A​​ DispatchGroup 添加 enter() 并减去 leave() 调用以确定工作何时完成。在您的情况下,您不会在触发您正在等待的任何异步调用之前进入该组。结果,您正在快速通过您的 enter()s 并且您的小组立即完成。

这里有一个小游乐场来说明这个想法:

import UIKit
import PlaygroundSupport
var str = "Hello, playground"

// Code will race to the notify before any enter() has been called.

func doAsyncWork_racing(completion: @escaping () -> Void) {
    let group = DispatchGroup()
    
    DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(3)) {
        for i in 0..<5 {
            group.enter()
            print("\(#function) doing task \(i)")
            group.leave()
        }
    }
    
    group.notify(queue: .main) {
        print("\(#function) done!!")
        completion()
    }
}

// Code will enter() before calling any async work, so the notify does 
// not get immediately fired off.

func doAsyncWork_correct(completion: @escaping () -> Void) {
    let group = DispatchGroup()
    group.enter()
    DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(3)) {
        for i in 0..<5 {
            group.enter()
            print("\(#function)  doing task \(i)")
            group.leave()
        }
        group.leave()
    }
    
    group.notify(queue: .main) {
        print("\(#function) done!!")
        completion()
    }
}

// Same as before, but work is submitted to a different queue and we
// block the thread with wait(), making the whole function blocking.

func doAsyncWork_waiting(completion: @escaping () -> Void) {
    let group = DispatchGroup()
    let workQueue = DispatchQueue(label: "work")

    // Here's an  example where the group waits until
    //  and block the thread its work is done.
    group.enter()
    workQueue.asyncAfter(deadline: .now() + .seconds(3)) {
        for i in 0..<5 {
            group.enter()
            print("\(#function)  doing task \(i)")
            group.leave()
        }
        group.leave()
    }
    
    group.wait()
    
    print("\(#function) done!!")
    completion()
}

doAsyncWork_racing() {
    doAsyncWork_correct() {
        doAsyncWork_waiting() {
            print("all calls done!")
        }
    }
}

PlaygroundSupport.PlaygroundPage.current.needsIndefiniteExecution = true