完成处理程序无法迅速正常运行

时间:2020-04-14 12:38:08

标签: ios swift

我试图用我从Firestore数据库中获得的数据填充两个数组。即时通讯成功获取数据,但是已经晚了,当我在viewDidLoad中打印它们时,它打印了空数组。所以我决定实现一个完成处理程序,但是它仍然显示和为空数组。谁能告诉我为什么即使我使用转义

,我的print语句也要在功能之前运行
func yourFunctionName(finished: @escaping () -> Void) {
    db.collection("countries")
        .whereField("capital", isEqualTo: "washington")
        .getDocuments { (snapshot, error) in
        if error == nil{
            for document in snapshot!.documents {
                let documentData = document.data()

                //print(document.documentID)
                //print(documentData)

                self.countries.append(document.documentID)
            }

        }
    }

    db.collection("countries")
        .whereField("climate", isEqualTo: "pleasant")
        .getDocuments { (snapshot, error) in
        if error == nil {
            for document in snapshot!.documents{
                let documentData = document.data()

                //print(document.documentID)
                //print(documentData)

                self.countries2.append(document.documentID)
            }
        }
    }
    finished()

}

viewDidLoad(){
    yourFunctionName {
        print(self.countries)
        print(self.countries2)
    }
}

我在输出中获得了空数组,尽管我应该使用@escaping调用im来打印数组。请有人在这里帮助我

4 个答案:

答案 0 :(得分:0)

由于Firebase的函数实际上是异步的(它们可以在函数“ yourFunctionName”完成工作之后运行),因此它们返回空数组。 为了使其按预期工作(打印填充的数组) 您需要做的就是在Firebase的闭包内部调用它,就像这样:

func yourFunctionName(finished: @escaping () -> Void) {
db.collection("countries")
    .whereField("capital", isEqualTo: "washington")
    .getDocuments { (snapshot, error) in
    if error == nil{
        for document in snapshot!.documents {
            let documentData = document.data()
            self.countries.append(document.documentID)
            finished() //<<< here
        }

    }
}

db.collection("countries")
    .whereField("climate", isEqualTo: "pleasant")
    .getDocuments { (snapshot, error) in
    if error == nil {
        for document in snapshot!.documents{
            let documentData = document.data()
            self.countries2.append(document.documentID)
            finished() //<<< and here
        }
    }
}

}

答案 1 :(得分:0)

自从遇到此问题以来已有一段时间,但我相信问题在于您正在调用完成处理程序。我的意思是,您浏览文档后可以尝试直接调用它。一种想法可能是在完成时返回它,或者只是按照您的方式做。尝试以下方法:

func yourFunctionName(finished: @escaping ([YourDataType]?) -> Void) {
        var countires: [Your Data Type] = []
        db.collection("countries")
            .whereField("capital", isEqualTo: "washington")
            .getDocuments { (snapshot, error) in
            if error == nil{
                for document in snapshot!.documents {
                    let documentData = document.data()
                    //print(document.documentID)
                    //print(documentData)
                    countries.append(document.documentID)
                }
               finished(countries)
               return
            }
        }
    }

    func yourSecondName(finished: @escaping([YouDataType]?) -> Void) {
    var countries: [Your data type] = []

    db.collection("countries")
            .whereField("climate", isEqualTo: "pleasant")
            .getDocuments { (snapshot, error) in
            if error == nil {

                for document in snapshot!.documents{
                    let documentData = document.data()
                    //print(document.documentID)
                    //print(documentData)
                   countires.append(document.documentID)
                }
              finished(countires)
              return
            }
        }

    func load() {
        yourFunctionName() { countries in
           print(countires)
        }
        yourSecondName() { countries in
           print(countries)
        }
   }

   viewDidLoad(){
        load()
    }

这将做的是,当您调用类型为@escaping的完成块并在其之后返回时,您将不再响应该完全块,因此将仅使用接收到的数据,因此不在乎关于该功能了。

根据我的一个好习惯,是将对象返回到完成块中,并使用单独的函数以使其更易于调试和更有效,并且还允许您使用@escaping返回,然后再返回。

您可以使用一个单独的方法来结合这两种方法并更新UI。如果要更新UI,请记住使用以下方法获取主队列:

DispathQueue.main.async {
    // Update the UI here
}

应该可以。伟大的问题,希望对您有所帮助!

答案 2 :(得分:0)

您实际上并未逃脱闭包。 对于我所知道的,“ @ escaping”是一个标签,该函数的开发人员使用该标签来表示使用该函数的人,他/她正在传递的闭包将被存储,并稍后(在函数结束后)调用以进行异步和存储管理。在您的情况下,您可以调用函数本身立即传递的闭包。因此,关闭并没有逃脱。

firebase数据库也是异步的。意味着您不会立即收到结果

这部分:

{ (snapshot, error) in
    if error == nil{
        for document in snapshot!.documents {
            let documentData = document.data()

            //print(document.documentID)
            //print(documentData)

            self.countries.append(document.documentID)
        }

    }
}

本身是一个闭包,将在生成查询结果时稍后执行。如您在文档中所见,该函数正在转义闭包:https://firebase.google.com/docs/reference/swift/firebasefirestore/api/reference/Classes/Query.html#getdocumentssource:completion

func getDocuments(source: FirestoreSource, completion: @escaping FIRQuerySnapshotBlock)

所以总结一下: Firebase查询的代码将在以后调用(但您不知道何时),并且在定义了Firebase回调之后(即被调用之前)立即调用闭包“ finished”。

您应该在firebase回调中调用完成的闭包,以在填充数组时使用它。

答案 3 :(得分:0)

我认为您的主要问题不是要填充您的arrays,而是要使它变得更好。

我举了一个例子,说明如何更好地做到这一点。

首先,将大function分成两部分,然后将其填充到function中。

看下面的代码,观察viewDidLoad的实现。

func countries(withCapital capital: String, completionHandler: (Result<Int, Error>) -> Void) {
        db.collection("countries")
        .whereField("capital", isEqualTo: capital)
        .getDocuments { (snapshot, error) in
            guard error == nil else {
                completionHandler(.failure(error!))
                return

            }

            let documents = snapshot!.documents
            let ids = documents.map { $0.documentID }
            completionHandler(.success(ids))
    }

}

func countries(withClimate climate: String, completionHandler: (Result<Int, Error>) -> Void) {
        db.collection("countries")
        .whereField("climate", isEqualTo: climate)
        .getDocuments { (snapshot, error) in
            guard error == nil else {
                completionHandler(.failure(error!))
                return

            }

            let documents = snapshot!.documents
            let ids = documents.map { $0.documentID }
            completionHandler(.success(ids))
    }
}


func viewDidLoad(){
    countries(withClimate: "pleasant") { (result) in
        switch result {
        case .success(let countries):
            print(countries)
            self.countries2 = countries
        default:
            break
        }
    }

    countries(withCapital: "washington") { (result) in
        switch result {
        case .success(let countries):
            print(countries)
            self.countries = countries
        default:
            break
        }
    }
}

如果必须使用它调用主线程调用

DispathQueue.main.async {
   // code here
}

我希望它能对您有所帮助。