首页 > 解决方案 > 第二次函数调用后才可用的值

问题描述

我有以下函数,它运行良好,但现在我需要函数中的值才能将其用于其他目的。但我的问题是我总是必须执行两次函数才能在函数的外部获得一个值(var valueOutOfFunction)。

var valueOutOfFunction = [(String)]()
func loadQuery(com:@escaping( [(Int, String)] -> ())){ 
    var arrayOfTuples = [(Int, String)]() 
    db.collection("Data").whereField("age", isGreaterThanOrEqualTo: 1).whereField("age", isLessThanOrEqualTo: 50).whereField("gender", isEqualTo: "F").getDocuments() { (querySnapshot, err) in
        if let err = err {
            print("Error getting documents: \(err)")
        } else {
                 for (index, document) in querySnapshot!.documents.enumerated() {
                 arrayOfTuples += [(index, document.documentID)]
                 }       
        }
        com(arrayOfTuples)
    } 
}

然后我在这里调用它:

loadQuery { arr in 
  self.valueOutOfFunction = arr // Has a value in the first execution
}  

print (valueOutOfFunction) //Executing the first time there is no value in the variable, the second time it have a value.

为什么它只是在第二次尝试可用,什么可以解决这个问题?谢谢!

标签: swiftfunctiongoogle-cloud-firestore

解决方案


那是因为valueOutOfFunction发生在那个被异步调用到 print 语句的闭包内部。此外,您正在执行else 语句之外的完成,该语句可能在您的 for 循环完成之前触发。您想要做的是从闭包本身控制流程,如下所示:


// move com(arrayOfTuples) up into the else block but outside of the for loop


func handleQuery() {
    loadQuery { doStuffWithResult($0) }
    // This line will run before the first line of doStuffWithResult
    // It would be best to end the function logic at loadQuery
}

func doStuffWithResult(_ arrayOfTuples: [(Int, String)]) {
    print(arrayOfTuples)
    // Do other work here
}

您正在研究的是控制流。你想执行X if Y已经发生了,Z if !Y对吗?

我建议查看swift 的结果类型。这将帮助您使用闭包管理控制流。

一个例子:

typealias QueryResult = [(Int, String)]

enum QueryErrors: Error {
    case couldNotFind
}

func loadQuery(_ result: @escaping (Result<QueryResult, Error>) -> Void) {
    db.collection("Data").whereField("age", isGreaterThanOrEqualTo: 1).whereField("age", isLessThanOrEqualTo: 50).whereField("gender", isEqualTo: "F").getDocuments() { (querySnapshot, err) in
        guard let documents = querySnapshot?.documents.enumerated() else {
            // I think enumerated will give you an array of tuples... if not add this to the end of that line. After enumerated but before else
            // enumerated().compactMap { ($0, $1) }
            result(.failure(err ?? QueryErrors.couldNotFind))// passes back Firebase error if it exists or default error if it doesn't
            return 
        } 
            result(.success(documents))
    }
}

// how it works 

loadQuery() { result in
    switch(result) {
    case .success(let arr): print(arr)
    case .failure(let error): print(error)
    }
}

推荐阅读