Значение доступно только после второго вызова функции - PullRequest
0 голосов
/ 16 февраля 2020

У меня есть следующая функция, и она отлично работает, но теперь мне нужно значение из функции, чтобы использовать ее для других целей. Но моя проблема в том, что мне всегда приходится выполнять функцию дважды, чтобы получить значение во внешней части функции (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.

Почему доступна только вторая попытка и как можно решить эту проблему? Спасибо!

1 Ответ

2 голосов
/ 16 февраля 2020

Это потому, что valueOutOfFunction происходит внутри того замыкания, которое вызывается асинхронно с оператором print. Кроме того, вы выполняете завершение вне оператора else , который может запускаться до того, как завершится ваш запрос l oop. То, что вы хотите сделать, это контролировать поток изнутри самого затвора следующим образом:


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