swift - 如何在 Swift 中使带有循环的函数异步?

标签 swift firebase asynchronous

我正在为图书馆创建一个应用程序。我试图获取用户从 Firebase checkout 的所有书籍,但我尝试使该函数与 DispatchGroup 异步似乎不起作用。我怀疑这是因为函数内部发现了 for-in 循环。

    func fetchHistory() {

    if items.count > 0 {
        items.removeAll()
    }

    let myGroup = DispatchGroup()
    myGroup.enter()

    var itemNames = [String]() // this holds the names of the child values of /users/uid/items/ <-- located in Firebase Database
    guard let uid = fAuth.currentUser?.uid else {return}
    fData.child("users").child(uid).child("items").observe(.value, with: { snapshot in

        // make sure there is at least ONE item in the history
        if snapshot.childrenCount > 0 {
            let values = snapshot.value as! NSDictionary
            for i in values.allKeys {
                itemNames.append(i as! String)
            }

            print(itemNames)
            let uid = fAuth.currentUser!.uid // get the UID of the user
            for item in itemNames {
                fData.child("users").child(uid).child("items").child(item).observe(.value, with: { snapshot in
                    let values = snapshot.value as! NSDictionary
                    let bookTitle = values["title"] as! String
                    print(bookTitle)
                    let bookAuthor = values["author"] as! String
                    print(bookAuthor)
                    let bookCoverUrl = values["coverUrl"] as! String
                    print(bookCoverUrl)
                    let bookStatus = values["status"] as! String
                    print(bookStatus)
                    let bookDueDate = values["dueDate"] as! String
                    print(bookDueDate)

                    let book = Book(name: bookTitle, author: bookAuthor, coverUrl: bookCoverUrl, status: bookStatus, dueDate: bookDueDate)
                    self.items.append(book)
                })
            }
            self.booksTable.isHidden = false
        } else {
            self.booksTable.isHidden = true
        }

    })

    myGroup.leave()
    myGroup.notify(queue: DispatchQueue.main, execute: {
        self.booksTable.reloadData()
        print("Reloading table")
    })

}

这是 print() 语句的输出:

########0
Reloading table
["78DFB90A-DE5B-47DE-ADCA-2DAB9D43B9C8"]
Mockingjay (The Hunger Games, #3)
Suzanne Collins
https://images.gr-assets.com/books/1358275419s/7260188.jpg
Checked
Replace

输出的前两行应该在其他所有内容打印之后打印。我真的需要一些帮助,我已经被困在这个问题上几个小时了。谢谢!

编辑: 根据要求,这是我的 Firebase 结构:

users:
  meZGWn5vhzXpk5Gsh92NhSasUPx2:
    ID: "12345"
    firstname: "Faraaz"
    items:
        78DFB90A-DE5B-47DE-ADCA-2DAB9D43B9C8
            author: "Suzanne Collins"
            coverUrl: "https://images.gr assets.com/books/1358275419s/..."
            dueDate: "Date"
            status: "Checked"
            title: "Mockingjay (The Hunger Games, #3)"
     type: "regular"

最佳答案

几个问题:

  1. 该模式是 leave 必须在异步调用的完成处理程序内调用。您希望这是闭包内执行的最后一件事,因此您可以将其添加为完成处理程序闭包内的最后一行。

    或者我更喜欢使用 defer 子句,这样您不仅知道它将是闭包中执行的最后一件事,而且还:

    • 即使您稍后在闭包中添加任何“提前退出”,也要确保您离开;和
    • enterleave 调用在代码中直观地显示在彼此旁边,使您不必在闭包的底部进行视觉搜索以确保它被正确调用。
  2. 如果您想在 for 循环中等待异步调用,也必须将其添加到此处。

  3. 这是一个非常小的问题,但您可能不希望在成功解包 uid 之前创建组。如果您可以返回并且不执行任何异步代码,为什么要创建DispatchGroup

因此,也许:

func fetchHistory() {

    if items.count > 0 {
        items.removeAll()
    }

    var itemNames = [String]()
    guard let uid = fAuth.currentUser?.uid else {return}

    let group = DispatchGroup()

    group.enter()

    fData.child("users").child(uid).child("items").observe(.value, with: { snapshot in

        defer { group.leave() }               // in case you add any early exits, this will safely capture

        if snapshot.childrenCount > 0 {
            ...
            for item in itemNames {

                group.enter()                 // also enter before we do this secondary async call

                fData.child("users").child(uid).child("items").child(item).observe(.value, with: { snapshot in

                    defer { group.leave() }   // and, again, defer the `leave`

                    ...
                })
            }
            ...
        } else {
            ...
        }
    })

    group.notify(queue: .main) {
        self.booksTable.reloadData()
        print("Reloading table")
    }    
}

关于swift - 如何在 Swift 中使带有循环的函数异步?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55461025/

相关文章:

ios - Swift 4 局部变量赋值

ios - 使用数组添加 Realm 对象

ios - 在 swift 3 中上传到 Firebase 存储之前调整 UIImage 的大小

android - 获取 cordova ionic 应用程序的 Firebase 事件参数

javascript - 使用 koa 2 和 mongoose 进行异步/等待

java - AsyncResponse ConnectionCallback 不会在 Jersey 中触发

html - 如何修复 webview 中调整视频 HTML 的大小?

swift - 这是一种在 Swift 中观察全局变量的方法吗?

IOS Firebase 后台获取

javascript - 将 setTimeout 添加到带有回调的函数的语法