如何在Swift中使带有循环的函数异步化? [英] How to make a function with a loop asynchronous in Swift?

查看:178
本文介绍了如何在Swift中使带有循环的函数异步化?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I am creating an application for a library. I am trying to fetch all the books the user has checked out from Firebase, but my attempts to make the function asynchronous with a DispatchGroup doesn't seem to be working. I suspect this to be because of the for-in loop found inside of the function.

    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()语句的输出:

Here is the output from the print() statements:

########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

输出的前两行应在打印完其他所有内容之后打印.我真的需要一些帮助,我已经坚持了几个小时.谢谢!

The first two lines of output should be printed AFTER everything else has printed. I really need some help on this, I have been stuck on this for hours. Thanks!

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

As requested, here is my Firebase structure:

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.您希望这是闭包内部执行的最后一件事,因此您可以将其添加为完成处理程序闭包内的最后一行.

  1. The pattern is that leave must be called inside the completion handler of the asynchronous call. You want this to be the last thing performed inside the closure, so you could add it as the the last line within completion handler closure.

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

Or I prefer to use a defer clause, so that not only do you know it will be the last thing performed in the closure, but also:

  • 即使以后再在封包中添加任何早期出口",您也要确保leave;和
  • enterleave调用在代码中直观地并排出现,从而使您不必从视觉上搜寻闭包底部以确保正确调用了它.
  • you ensure you leave even if you later add any "early exits" inside your closure; and
  • the enter and leave calls visually appear right next to each other in the code saving you from having to visually hunt down at the bottom of the closure to make sure it was called correctly.

此外,如果要等待for循环中的异步调用,也必须在其中添加它.

You also, if you want to wait for the asynchronous calls in the for loop, have to add it there, too.

非常重要的一点,但是您可能要在成功解包uid之前不创建组.如果可能return并且不执行任何异步代码,为什么还要创建DispatchGroup?

A very minor point, but you might want to not create the group until you successfully unwrapped uid. Why create the DispatchGroup if you could possibly return and not do any of the asynchronous code?

因此,也许:

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中使带有循环的函数异步化?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆