Dispatch.notify вызывается до завершения задачи? - PullRequest
0 голосов
/ 10 июля 2020

Я пытаюсь загрузить данные из Firestore, добавить их в массив, а затем добавить в другой массив, как только все данные будут загружены. Я пробовал использовать обработчик завершения, но он возвращается, даже если не все данные были загружены. Итак, я попытался использовать DispatchGroup, однако dispatch.notify вызывается до завершения моей задачи.

вот мой код. Я вызываю dispatch.begin внутри последней скобки else, иначе по какой-то причине left и 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: { $0.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: { $0.uid == Info.uid }) || Info.uid == Auth.auth().currentUser?.uid || self.sameUniRelatedCourses.contains(where: { $0.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)
        }

}

Ответы [ 2 ]

2 голосов
/ 10 июля 2020

Вам нужно использовать Dispatch Group вот так. Введите перед выполнением асинхронной задачи и оставьте вызов внутри закрытия завершения

 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: { $0.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: { $0.uid == Info.uid }) || Info.uid == Auth.auth().currentUser?.uid || self.sameUniRelatedCourses.contains(where: { $0.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)
            }

    }
0 голосов
/ 10 июля 2020

A DispatchGroup складывает enter() и вычитает leave() вызовов, чтобы определить, когда работа будет выполнена. В вашем случае вы не входите в группу до того, как отмените какие-либо асинхронные c вызовы, которые вы ожидаете. В результате вы проскакиваете мимо ваших enter (), и ваша группа немедленно финиширует.

Вот небольшая площадка, чтобы проиллюстрировать идею:

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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...