Is Firebase getDocument run line by line?

ε祈祈猫儿з 提交于 2021-02-05 08:01:30

问题


I'm a swift and Firebase beginner, I met a problem that confuses me a lot. We all know the following code

var a = [Int]()
for i in 0..2{
    a.append(i)
    print(a)
print(a)

should give me the output

[0]
[0,1]
[0,1,2]
[0,1,2]

And it is true as the result. However, when I work on getDocument(), and I write a similar logic code, the output is strange! The code is the following.

override func viewDidLoad() {
    super.viewDidLoad()
    let docSecond = getThirtyData()
    getDataContent(thirtyData: docSecond)
}
func getThirtyData()->Array<Int>{
    var querySecond = [Int]()
    var docSecond = [Int]()
    let postDoc = db.collection("announcement")
    postDoc.getDocuments { (querySnapshot, err) in
        if let err = err{
            print("Error getting documents: \(err)")
        }else{
            for document in querySnapshot!.documents{
                //print(document.data()["postTimeSecondInt"] as! Int)
                querySecond.append(document.data()["postTimeSecondInt"] as! Int)
            }
            for _ in 1...querySecond.count{
                let max = querySecond.max()!
                docSecond.append(max)
                let index = querySecond.firstIndex(of: max)
                querySecond.remove(at: index!)
            }
            print(docSecond)
            print("123")
        }
    }
    print(docSecond)
    print("456")
    return docSecond
}

func getDataContent(thirtyData: [Int]){
    print(thirtyData)
    print("789")
}

I thought the result might come as same logic which is "123" print first, then "456", then "789". However, the result is like below.

[]
456
[]
789
[1588428987, 1588428980, 1588427392]
123

Seems like it run the code line below simultaneously with the for loop. Can anyone explain why this happens?


回答1:


This is working as intended/documented, but if you've never used an asynchronous API it can indeed be confusing. The reason that the code executes out of order is that postDoc.getDocuments() has to read data from the server, which may take time. Instead of blocking your code (and thus keeping the user from using your app), your main code is allowed to continue. And then when the data is available from the server, your completion handler is called with that data.

It's easiest to see this by adding some logging statements:

print("Before starting to load data")
let postDoc = db.collection("announcement")
postDoc.getDocuments { (querySnapshot, err) in
    print("Got data");
}
print("After starting to load data")

When you run this minimal code, it prints:

Before starting to load data

After starting to load data

Got data

This is working as intended, but indeed very confusing if you've never worked with asynchronous or event driven APIs yet. Unfortunately most modern cloud/web APIs are asynchronous, so it's best to get used to this pattern. Hopefully my explanation above help a bit for that.

Two important things to remember:

  • Your print(docSecond) doesn't print the data you want, because docSecond.append(max) hasn't run yet.
  • Any code that needs data from the database, needs to be inside the completion handler that is called with that data (or be called from there).

You can't return data that is loaded asynchronously. The typical workaround is to create a custom completion handler, very similar to the completion handler that Firestore's getDocuments() accepts postDoc.getDocuments { (querySnapshot, err) in, but then with your own type.

Something like:

func getThirtyData(_ completion: (Array<Int>) -> ()) {
    var querySecond = [Int]()
    var docSecond = [Int]()
    let postDoc = db.collection("announcement")
    postDoc.getDocuments { (querySnapshot, err) in
        if let err = err{
            print("Error getting documents: \(err)")
        }else{
            for document in querySnapshot!.documents{
                querySecond.append(document.data()["postTimeSecondInt"] as! Int)
            }
            for _ in 1...querySecond.count{
                let max = querySecond.max()!
                docSecond.append(max)
                let index = querySecond.firstIndex(of: max)
                querySecond.remove(at: index!)
            }
            completion(docSecond)
        }
    }
}

And then call it like:

getThirtyData { docSecond in
   getDataContent(thirtyData: docSecond)
}

See also:

  • Swift Firestore Search for users
  • Returning name from Firestore?
  • Asign value of a Firestore document to a variable
  • Waiting for Asynchronous function call to complete


来源:https://stackoverflow.com/questions/61561868/is-firebase-getdocument-run-line-by-line

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!