【问题标题】:I want my code to run consecutively/synchronously in the background (DispatchQueue)我希望我的代码在后台连续/同步运行(DispatchQueue)
【发布时间】:2020-05-02 22:01:33
【问题描述】:

我希望 grabAllFollowingPosts() 仅在 loadFollowing() 完成运行后运行。这些都是网络调用,所以我想在后台运行它们。关于我的代码为什么不起作用的任何想法?

DispatchQueue.global(qos: .userInteractive).sync {
    self.loadFollowing()
    self.grabAllFollowingPosts()
    DispatchQueue.main.async {
        self.tableView.reloadData()
    }
}

这三个函数的作用是:

  1. 抓取当前用户关注的每个用户
  2. 为每个用户获取他们的帖子

因此,loadUsers() 必须在 grabAllFollowingPosts() 之前运行

var followingUsers = [String]()

//Function 1: load the poeple you are following into the followingUsers array
func loadFollowing () {
    guard let userID = Auth.auth().currentUser?.uid else { return }
    let firestoreRef = Firestore.firestore().collection("Following").document(userID).collection("UserFollowing")
    firestoreRef.addSnapshotListener { (snapshot, error) in

        if error != nil {
            //error retrieving documents
            print (error!.localizedDescription)
        } else {
            // document retrival successful
            guard let snapshot = snapshot else { return }
            for document in snapshot.documents {

                let data = document.data()
                let userid = data["UserID"] as? String ?? "anonymous"

                self.followingUsers.append(userid)
            }
        }
    }
}


//Function 2: for all of the users in the followingUsers array - grab their documents from Firestore
func grabAllFollowingPosts () {
    for users in followingUsers {
        loadPosts(theUsers: users)
    }
}

//Function 3: loads the posts
func loadPosts (theUsers: String) {
    let firestoreRef = Firestore.firestore().collection("Posts").whereField("UserID", isEqualTo: theUsers).whereField("Date", isGreaterThanOrEqualTo: Date()).limit(to: 8)
    //TODO: add infinate scroll
    firestoreRef.addSnapshotListener { (snapshot, error) in
        if error != nil {
            //error retrieving documents
            print (error!.localizedDescription)
        } else {
            // document retrival successful
            guard let snapshot = snapshot else { return }
            for document in snapshot.documents {
                let data = document.data()
                let ageRestriction = data["AgeRestriction"] as? String ?? "Age E"
                let category = data["Category"] as? String ?? "Error - No Category"
                let date = data["Date"] as? Date ?? Date()
                let documentId = data["DocumentID"] as? String ?? "Error - No Document-ID"
                let description = data["Description"] as? String ?? "Error - No Description"
                let location = data["Location"] as? String ?? "Error - No Location"
                let title = data["Title"] as? String ?? "Error - No Title"
                let userId = data["UserID"] as? String ?? "Error - No User-ID"
                let username = data["Username"] as? String ?? "Anonymous"
                let color = data["Color"] as? String ?? "Sale"

                let newPost = Post(documentIDText: documentId, usernameText: username, titleText: title, locationText: location, dateText: date, descriptionText: description, ageText: ageRestriction, category: category, uid: userId, color: color)
                self.posts.append(newPost)
            }
            if self.posts.isEmpty {self.goFollowPeopleImage.isHidden = false}

        }
    }
}

【问题讨论】:

  • 请发帖loadFollowing()函数和grabAllFollowingPosts
  • @Rob 这是您要求的 2 个功能
  • 顺便说一下,分发到全局队列是不必要的,因为这些方法已经异步运行。如果你需要调度到全局队列,你应该使用async,而不是sync

标签: swift queue grand-central-dispatch dispatch


【解决方案1】:

有两种基本模式:

  1. 在处理 RESTful 网络请求时,我们为所有网络例程提供了一个完成处理程序闭包,我们在网络请求完成时调用它。这样,调用者可以调用前一步的完成处理程序中的每个后续步骤。

    这个主题有很多变体(异步Operation 子类、futures/promises 等),但想法是相同的,即将一系列异步任务链接在一起,这样调用者就可以知道何时请求全部完成,可以触发 UI 更新。

  2. 另一方面,在处理 Firestore 时,我们可以添加观察者/侦听器以在更新到来时更新我们的 UI。addSnapshotListener 闭包在底层数据库更新时被重复调用。在这种情况下,没有“好的,我们完成了,更新 UI”的时间点(因此我们通常不会使用完成处理程序方法),而是我们只是在文档进入时不断更新 UI .

但是,当您的示例使用 addSnapshotListener 时,它也使用了 limit(to:),这会增加皱纹。这有点像第一个场景(例如,如果您限制为 8,并且您检索到 8,则不会再次调用侦听器)。但这也有点像第二种情况(例如,如果限制为 8 个并且您当前只有 7 个帖子,它将检索前 7 个并调用该闭包;但如果有另一条记录进入,它将再次调用该闭包,这8th 文档的时间)。

尝试同时处理有限/分页响应和侦听实时更新可能会变得复杂。我可能会建议,如果您想让 Firestore 像 RESTful 服务一样工作,我可能会建议使用 getDocuments 而不是 addSnapshotListener,从而消除这种复杂性。然后您可以使用其他人推荐的完成处理程序方法。它使它的行为有点像 RESTful 方法(但是,你又失去了实时更新功能)。


如果您想知道第二个实时场景可能是什么样子,这里有一个简化的示例(我的帖子只有“文本”和“日期”属性,但希望它能够说明该过程):

func addPostsListener() {
    db.collection("posts").addSnapshotListener { [weak self] snapshot, error in
        guard let self = self else { return }

        guard let snapshot = snapshot, error == nil else {
            print(error ?? "Unknown error")
            return
        }

        for diff in snapshot.documentChanges {
            let document = diff.document

            switch diff.type {
            case .added:    self.add(document)
            case .modified: self.modify(document)
            case .removed:  self.remove(document)
            }
        }
    }
}

func add(_ document: QueryDocumentSnapshot) {
    guard let post = post(for: document) else { return }
    let indexPath = IndexPath(item: self.posts.count, section: 0)
    posts.append(post)
    tableView.insertRows(at: [indexPath], with: .automatic)
}

func modify(_ document: QueryDocumentSnapshot) {
    guard let row = row(for: document) else { return }
    guard let post = post(for: document) else { return }
    posts[row] = post
    tableView.reloadRows(at: [IndexPath(row: row, section: 0)], with: .automatic)
}

func remove(_ document: QueryDocumentSnapshot) {
    guard let row = row(for: document) else { return }
    posts.remove(at: row)
    tableView.deleteRows(at: [IndexPath(row: row, section: 0)], with: .automatic)
}

func row(for document: QueryDocumentSnapshot) -> Int? {
    posts.firstIndex {
        $0.id == document.documentID
    }
}

func post(for document: QueryDocumentSnapshot) -> Post? {
    let data = document.data()

    guard
        let text = data["text"] as? String,
        let timestamp = data["date"] as? Timestamp
    else {
        return nil
    }

    return Post(id: document.documentID, text: text, date: timestamp.dateValue())
}

但这种方法有效,因为我没有限制响应。如果您确实使用了limit(to:)limit(toLast:),那么当您达到该限制时,您将停止获取实时更新。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-12-29
    • 1970-01-01
    • 1970-01-01
    • 2020-11-23
    • 2019-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多