在 Swift 中链接 Promise 以初始化自定义对象

Posted

技术标签:

【中文标题】在 Swift 中链接 Promise 以初始化自定义对象【英文标题】:Chaining promises in Swift to initialize a custom object 【发布时间】:2019-11-05 18:18:02 【问题描述】:

我在 Swift 中实现了PromiseKit 以避免带有完成块的回调地狱。我需要知道将 Promise 链接在一起以初始化具有其他关联对象的自定义对象的最佳方法。例如,Comment 对象附加了一个 User 对象。

首先我从数据库中获取comments,它们在数据库结构中都有uid 属性。我最终希望得到一个comments 数组,每个数组都附加了正确的用户,所以我可以同时加载commentuser 数据。使用完成块,这一切似乎都容易得多,但我是一个总 Promise noob 所以 idk。

这是控制器中处理 fetch 的代码

CommentsService.shared.fetchComments(withPostKey: postKey)
            .then  comments -> Promise<[User]> in
                let uids = comments.map( $0.uid )
                return UserService.shared.fetchUsers(withUids: uids)
        .done( users in
            // how to init Comment object with users now?
        )
            .catch  error in
                print("DEBUG: Failed with error \(error)")
        

这里是评论获取功能:

func fetchComments(withPostKey postKey: String) -> Promise<[Comment]> 
        return Promise  resolver in
            REF_COMMENTS.child(postKey).observeSingleEvent(of: .value)  snapshot in
                guard let dictionary = snapshot.value as? [String: AnyObject] else  return 
                let data = Array(dictionary.values)
                do 
                    let comments = try FirebaseDecoder().decode([Comment].self, from: data)
                    resolver.fulfill(comments)
                 catch let error 
                    resolver.reject(error)
                
            
        
    

这里是获取用户功能

func fetchUsers(withUids uids: [String]) -> Promise<[User]> 
        var users = [User]()

        return Promise  resolver in
            uids.forEach  uid in
                self.fetchUser(withUid: uid).done  user in
                    users.append(user)
                    guard users.count == uids.count else  return 
                    resolver.fulfill(users)
                .catch  error in
                    resolver.reject(error)
                
            
        
    

这里是评论对象:

struct Comment: Decodable 
    let uid: String
    let commentText: String
    let creationDate: Date
    var user: User?

完成块就是这么简单,开始认为 Promise 不值得?

func fetchComments(withPostKey postKey: String, completion: @escaping([Comment]) -> Void) 
        var comments = [Comment]()
        REF_COMMENTS.child(postKey).observe(.childAdded)  (snapshot) in

            guard let dictionary = snapshot.value as? [String: AnyObject] else  return 
            guard let uid = dictionary["uid"] as? String else  return 

            UserService.shared.fetchUser(withUid: uid, completion:  (user) in
                let comment = Comment(user: user, dictionary: dictionary)
                comments.append(comment)
                completion(comments)
            )
        
    

【问题讨论】:

我更喜欢 Rx 而不是 Promise 工具包,但在任何情况下,then 块在概念上就像一个 map,如果你接受一个 Promise&lt;T&gt; 但返回一个 U 然后他输出是另一个Promise&lt;U&gt;then 也可以像 flatMap 一样,如果你有一个 Promise&lt;T&gt; 但返回一个 Promise&lt;U&gt;,那么输出是 Promise&lt;U&gt; 而不是 Promise &lt;Promise&lt;U&gt;&gt;。在任何情况下,您都希望 a 从用户开始,因此返回 Promise&lt;User&gt; 或只是一个`用户`,然后您想要获取 cmets,因此从下一个 then block 返回 Promise&lt;Comments&gt; 或只是 Comments 我需要评论结构中的userIds,以便知道要获取哪些用户,因此必须先获取 cmets 【参考方案1】:

好的,我想我明白你在做什么了。问题是您需要与用户一起捕获 cmets,以便您可以一起返回,然后再将它们组合起来。它应该看起来像这样:

CommentsService.shared.fetchComments(withPostKey: postKey)
  .then  comments -> Promise<[Comment], [User]> in
    let uids = comments.map( $0.uid )
    return UserService.shared.fetchUsers(withUids: uids)
      .then  users in
        return Promise<[Comment], [User]>(comments, users)
      
    .done( combined in
      let (comments, users) = combined
      //Do combiney stuff here
    )
      .catch  error in
        print("DEBUG: Failed with error \(error)")

转换是 [评论] -> [用户] -> ([评论], [用户]) -> [附有用户的评论]

【讨论】:

以上是关于在 Swift 中链接 Promise 以初始化自定义对象的主要内容,如果未能解决你的问题,请参考以下文章

《Promise学习笔记》- 4Promise自定义封装之thencatch的封装

Swift:不能以编程方式配置自定义表格单元格?没有调用初始化?

如何在 Swift 1.2 中初始化自定义 UIView

使用自定义初始化程序 swift 以编程方式实例化和推送视图控制器

如何在 iOS 8/Swift 中构建自定义视图控制器的呈现?变量初始化被证明是困难的

Swift - 以编程方式创建的按钮和标签相互链接