问题描述
我正在为图书馆创建一个应用程序.我正在尝试从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"
推荐答案
几个问题:
-
模式是必须在异步调用的完成处理程序内 内调用
leave
.您希望这是闭包内部执行的最后一件事,因此您可以将其添加为完成处理程序闭包内的最后一行.
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
;和 -
enter
和leave
调用在代码中直观地并排出现,从而使您不必从视觉上搜寻闭包底部以确保正确调用了它.
- you ensure you
leave
even if you later add any "early exits" inside your closure; and - the
enter
andleave
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中使带有循环的函数异步化?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!