我想让我的代码在后台连续异步运行(DispatchQueue)。

问题描述 投票:1回答:1

我想 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}

        }
    }
}
swift xcode queue dispatch
1个回答
-1
投票

有两种基本模式。

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

    关于这个主题有很多变体(异步的)。Operation 子类、futurespromises等),但想法是一样的,即把一系列异步任务链在一起,这样调用者可以知道什么时候请求都完成了,并可以触发UI更新。

  2. 另一方面,在处理Firestore时,我们可以添加observerslisteners来在更新到来时更新我们的UI。这个 addSnapshotListener 闭包被反复调用,因为底层数据库被更新。在这种情况下,并没有一个 "好了,我们完成了,更新UI "的时间点(所以我们一般不会使用完成处理程序的方法),而只是在文档进来的时候不断地更新UI。

但是,虽然你的例子使用的是 addSnapshotListener它还在使用 limit(to:),这就增加了一个皱纹。它有点像第一种情况(比如,如果限制到8个,你检索了8个,监听器就不会再被调用)。但它也有点像第二种情况(例如,如果限制为8条,而你目前只有7条帖子,它会检索前7条,并调用该关闭;但如果又有一条记录进来,它会再次调用该关闭,这次是8条 文档)。)

试图处理有限的分页响应和监听实时更新可能会变得复杂。我建议,如果你想让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:),那么当你达到这个极限时,你就会停止获得实时更新。

© www.soinside.com 2019 - 2024. All rights reserved.