在异步工作完成之前关闭返回数据
Closure returning data before async work is done
更新了建议的解决方案和其他问题
我正式卡住了,也陷入了回调地狱。我打电话给 Firebase 以检索 FireStore 中的所有文章。在每个文章对象中都有一个图像文件名,该文件名转换为需要传递给函数以获取绝对 URL 返回的存储引用位置。我会将 URL 存储在数据中,但它可能会改变。问题是 ArticleListener 函数在没有所有数据的情况下过早地返回闭包 (returnArray),我不知道我遗漏了什么。在我添加 self.getURL 代码之前,这工作正常,但现在它返回数组为空,然后完成所有工作。
如果有人在这里有一些关于将方法链接在一起而不求助于 PromiseKit 或 GCD 的奖励提示,那会很棒,但欢迎所有建议以使其按原样工作
and/or 重构以提高效率/可读性!
使用 GCD 和更新示例的建议解决方案
这是在创建文章后调用作者初始化。我正在尝试转换 dataDict 字典,以便它在 Author init 期间用于键 ["author"]。我想我已经接近了,但不能 100% 确定我的 GCD enter/leave 调用是否按正确的顺序进行
public func SetupArticleListener(completion: @escaping ([Article]) -> Void) {
var returnArray = [Article]()
let db = FIRdb.articles.reference()
let listener = db.addSnapshotListener() { (querySnapshot, error) in
returnArray = [] // nil this out every time
if let error = error {
print("Error in setting up snapshot listener - \(error)")
} else {
let fireStoreDispatchGrp = DispatchGroup() /// 1
querySnapshot?.documents.forEach {
var dataDict = [=10=].data() //mutable copy of the dictionary data
let id = [=10=].documentID
//NEW EXAMPLE WITH ADDITIONAL TASK HERE
if let author = [=10=].data()["author"] as? DocumentReference {
author.getDocument() {(authorSnapshot, error) in
fireStoreDispatchGrp.enter() //1
if let error = error {
print("Error getting Author from snapshot inside Article getDocumentFunction - leaving dispatch group and returning early")
fireStoreDispatchGrp.leave()
return
}
if let newAuthor = authorSnapshot.flatMap(Author.init) {
print("Able to build new author \(newAuthor)")
dataDict["author"] = newAuthor
dataDict["authorId"] = authorSnapshot?.documentID
print("Data Dict successfully mutated \(dataDict)")
}
fireStoreDispatchGrp.leave() //2
}
}
///END OF NEW EXAMPLE
if let imageURL = [=10=].data()["image"] as? String {
let reference = FIRStorage.articles.referenceForFile(filename: imageURL)
fireStoreDispatchGrp.enter() /// 2
self.getURL(reference: reference){ result in
switch result {
case .success(let url) :
dataDict["image"] = url.absoluteString
case .failure(let error):
print("Error getting URL for author: \n Error: \(error) \n forReference: \(reference) \n forArticleID: \(id)")
}
if let newArticle = Article(id: id, dictionary: dataDict) {
returnArray.append(newArticle)
}
fireStoreDispatchGrp.leave() ///3
}
}
}
//Completion block
print("Exiting dispatchGroup all data should be setup correctly")
fireStoreDispatchGrp.notify(queue: .main) { ///4
completion(returnArray)
}
}
}
updateListeners(for: listener)
}
原码
调用设置代码
self.manager.SetupArticleListener() { [weak self] articles in
print("In closure function to update articles")
self?.articles = articles
}
文章监听器
public func SetupArticleListener(completion: @escaping ([Article]) -> Void) {
var returnArray = [Article]()
let db = FIRdb.articles.reference()
let listener = db.addSnapshotListener() { (querySnapshot, error) in
returnArray = [] // nil this out every time
if let error = error {
printLog("Error retrieving documents while adding snapshotlistener, Error: \(error.localizedDescription)")
} else {
querySnapshot?.documents.forEach {
var dataDict = [=12=].data() //mutable copy of the dictionary data
let id = [=12=].documentID
if let imageURL = [=12=].data()["image"] as? String {
let reference = FIRStorage.articles.referenceForFile(filename: imageURL)
self.getURL(reference: reference){ result in
switch result {
case .success(let url) :
print("Success in getting url from reference \(url)")
dataDict["image"] = url.absoluteString
print("Dictionary XFORM")
case .failure(let error):
print("Error retrieving URL from reference \(error)")
}
if let newArticle = Article(id: id, dictionary: dataDict) {
printLog("Success in creating Article with xformed url")
returnArray.append(newArticle)
}
}
}
}
print(" sending back completion array \(returnArray)")
completion(returnArray)
}
}
updateListeners(for: listener)
}
得到URL
private func getURL(reference: StorageReference, _ result: @escaping (Result<URL, Error>) -> Void) {
reference.downloadURL() { (url, error) in
if let url = url {
result(.success(url))
} else {
if let error = error {
print("error")
result(.failure(error))
}
}
}
}
您需要调度组,因为 for 循环包含多个异步调用
public func SetupArticleListener(completion: @escaping ([Article]) -> Void) {
var returnArray = [Article]()
let db = FIRdb.articles.reference()
let listener = db.addSnapshotListener() { (querySnapshot, error) in
returnArray = [] // nil this out every time
if let error = error {
printLog("Error retrieving documents while adding snapshotlistener, Error: \(error.localizedDescription)")
} else {
let g = DispatchGroup() /// 1
querySnapshot?.documents.forEach {
var dataDict = [=10=].data() //mutable copy of the dictionary data
let id = [=10=].documentID
if let imageURL = [=10=].data()["image"] as? String {
let reference = FIRStorage.articles.referenceForFile(filename: imageURL)
g.enter() /// 2
self.getURL(reference: reference){ result in
switch result {
case .success(let url) :
print("Success in getting url from reference \(url)")
dataDict["image"] = url.absoluteString
print("Dictionary XFORM")
case .failure(let error):
print("Error retrieving URL from reference \(error)")
}
if let newArticle = Article(id: id, dictionary: dataDict) {
printLog("Success in creating Article with xformed url")
returnArray.append(newArticle)
}
g.leave() /// 3
}
}
}
g.notify(queue:.main) { /// 4
print(" sending back completion array \(returnArray)")
completion(returnArray)
}
}
}
updateListeners(for: listener)
}
更新了建议的解决方案和其他问题
我正式卡住了,也陷入了回调地狱。我打电话给 Firebase 以检索 FireStore 中的所有文章。在每个文章对象中都有一个图像文件名,该文件名转换为需要传递给函数以获取绝对 URL 返回的存储引用位置。我会将 URL 存储在数据中,但它可能会改变。问题是 ArticleListener 函数在没有所有数据的情况下过早地返回闭包 (returnArray),我不知道我遗漏了什么。在我添加 self.getURL 代码之前,这工作正常,但现在它返回数组为空,然后完成所有工作。
如果有人在这里有一些关于将方法链接在一起而不求助于 PromiseKit 或 GCD 的奖励提示,那会很棒,但欢迎所有建议以使其按原样工作 and/or 重构以提高效率/可读性!
使用 GCD 和更新示例的建议解决方案
这是在创建文章后调用作者初始化。我正在尝试转换 dataDict 字典,以便它在 Author init 期间用于键 ["author"]。我想我已经接近了,但不能 100% 确定我的 GCD enter/leave 调用是否按正确的顺序进行
public func SetupArticleListener(completion: @escaping ([Article]) -> Void) {
var returnArray = [Article]()
let db = FIRdb.articles.reference()
let listener = db.addSnapshotListener() { (querySnapshot, error) in
returnArray = [] // nil this out every time
if let error = error {
print("Error in setting up snapshot listener - \(error)")
} else {
let fireStoreDispatchGrp = DispatchGroup() /// 1
querySnapshot?.documents.forEach {
var dataDict = [=10=].data() //mutable copy of the dictionary data
let id = [=10=].documentID
//NEW EXAMPLE WITH ADDITIONAL TASK HERE
if let author = [=10=].data()["author"] as? DocumentReference {
author.getDocument() {(authorSnapshot, error) in
fireStoreDispatchGrp.enter() //1
if let error = error {
print("Error getting Author from snapshot inside Article getDocumentFunction - leaving dispatch group and returning early")
fireStoreDispatchGrp.leave()
return
}
if let newAuthor = authorSnapshot.flatMap(Author.init) {
print("Able to build new author \(newAuthor)")
dataDict["author"] = newAuthor
dataDict["authorId"] = authorSnapshot?.documentID
print("Data Dict successfully mutated \(dataDict)")
}
fireStoreDispatchGrp.leave() //2
}
}
///END OF NEW EXAMPLE
if let imageURL = [=10=].data()["image"] as? String {
let reference = FIRStorage.articles.referenceForFile(filename: imageURL)
fireStoreDispatchGrp.enter() /// 2
self.getURL(reference: reference){ result in
switch result {
case .success(let url) :
dataDict["image"] = url.absoluteString
case .failure(let error):
print("Error getting URL for author: \n Error: \(error) \n forReference: \(reference) \n forArticleID: \(id)")
}
if let newArticle = Article(id: id, dictionary: dataDict) {
returnArray.append(newArticle)
}
fireStoreDispatchGrp.leave() ///3
}
}
}
//Completion block
print("Exiting dispatchGroup all data should be setup correctly")
fireStoreDispatchGrp.notify(queue: .main) { ///4
completion(returnArray)
}
}
}
updateListeners(for: listener)
}
原码
调用设置代码
self.manager.SetupArticleListener() { [weak self] articles in
print("In closure function to update articles")
self?.articles = articles
}
文章监听器
public func SetupArticleListener(completion: @escaping ([Article]) -> Void) {
var returnArray = [Article]()
let db = FIRdb.articles.reference()
let listener = db.addSnapshotListener() { (querySnapshot, error) in
returnArray = [] // nil this out every time
if let error = error {
printLog("Error retrieving documents while adding snapshotlistener, Error: \(error.localizedDescription)")
} else {
querySnapshot?.documents.forEach {
var dataDict = [=12=].data() //mutable copy of the dictionary data
let id = [=12=].documentID
if let imageURL = [=12=].data()["image"] as? String {
let reference = FIRStorage.articles.referenceForFile(filename: imageURL)
self.getURL(reference: reference){ result in
switch result {
case .success(let url) :
print("Success in getting url from reference \(url)")
dataDict["image"] = url.absoluteString
print("Dictionary XFORM")
case .failure(let error):
print("Error retrieving URL from reference \(error)")
}
if let newArticle = Article(id: id, dictionary: dataDict) {
printLog("Success in creating Article with xformed url")
returnArray.append(newArticle)
}
}
}
}
print(" sending back completion array \(returnArray)")
completion(returnArray)
}
}
updateListeners(for: listener)
}
得到URL
private func getURL(reference: StorageReference, _ result: @escaping (Result<URL, Error>) -> Void) {
reference.downloadURL() { (url, error) in
if let url = url {
result(.success(url))
} else {
if let error = error {
print("error")
result(.failure(error))
}
}
}
}
您需要调度组,因为 for 循环包含多个异步调用
public func SetupArticleListener(completion: @escaping ([Article]) -> Void) {
var returnArray = [Article]()
let db = FIRdb.articles.reference()
let listener = db.addSnapshotListener() { (querySnapshot, error) in
returnArray = [] // nil this out every time
if let error = error {
printLog("Error retrieving documents while adding snapshotlistener, Error: \(error.localizedDescription)")
} else {
let g = DispatchGroup() /// 1
querySnapshot?.documents.forEach {
var dataDict = [=10=].data() //mutable copy of the dictionary data
let id = [=10=].documentID
if let imageURL = [=10=].data()["image"] as? String {
let reference = FIRStorage.articles.referenceForFile(filename: imageURL)
g.enter() /// 2
self.getURL(reference: reference){ result in
switch result {
case .success(let url) :
print("Success in getting url from reference \(url)")
dataDict["image"] = url.absoluteString
print("Dictionary XFORM")
case .failure(let error):
print("Error retrieving URL from reference \(error)")
}
if let newArticle = Article(id: id, dictionary: dataDict) {
printLog("Success in creating Article with xformed url")
returnArray.append(newArticle)
}
g.leave() /// 3
}
}
}
g.notify(queue:.main) { /// 4
print(" sending back completion array \(returnArray)")
completion(returnArray)
}
}
}
updateListeners(for: listener)
}