【问题标题】:Closure returning data before async work is done在异步工作完成之前关闭返回数据
【发布时间】:2020-04-08 06:21:31
【问题描述】:

更新了提出的解决方案和附加问题

我正式陷入困境,也陷入了回调地狱。我打电话给 Firebase 以检索 FireStore 中的所有文章。每个文章对象内部都有一个图像文件名,该文件名转换为存储引用位置,需要将其传递给函数以获取绝对 URL。我会将 URL 存储在数据中,但它可能会改变。问题是 ArticleListener 函数过早返回没有所有数据的闭包 (returnArray) ,我无法弄清楚我错过了什么。这在我添加 self.getURL 代码之前工作正常,但现在它将数组返回为空,然后完成所有工作。

如果有人在这里有一些关于将方法链接在一起而不诉诸 PromiseKit 或 GCD 的额外提示,那将是很棒的,但欢迎所有建议使其按原样工作 和/或重构以提高效率/可读性!

建议的 GCD 解决方案和更新示例

这是在创建文章后调用作者 init。我正在尝试转换 dataDict 字典,以便在 Author 初始化期间用于键 [“author”]。我想我已经接近了,但不能 100% 确定我的 GCD 进入/离开呼叫是否以正确的顺序发生

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 = $0.data() //mutable copy of the dictionary data
                    let id = $0.documentID

//NEW EXAMPLE WITH ADDITIONAL TASK HERE
                    if let author = $0.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 = $0.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 = $0.data() //mutable copy of the dictionary data
                    let id = $0.documentID
                    if let imageURL = $0.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)
    }

获取网址

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))
                }
            }

        }
    }

【问题讨论】:

    标签: ios swift firebase-realtime-database firebase-storage grand-central-dispatch


    【解决方案1】:

    您需要调度组,因为 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 = $0.data() //mutable copy of the dictionary data
                    let id = $0.documentID
                    if let imageURL = $0.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)
    }
    

    【讨论】:

    • 谢谢@Sh_Khan。这解决了我的直接问题,不胜感激!这里还有一个问题,如果我添加另一个异步函数(假设我想从返回的 DocumentReference -> Snapshot 块中初始化相应的 Author 对象,我是否使用另一个进入/离开调用来增加调度组?我假设是并且想要确认我应该在执行异步调用之前调用 enter right,并在所有工作完成后离开 RIGHT。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多