【问题标题】:Parallel async execution in swift loop快速循环中的并行异步执行
【发布时间】:2022-11-13 05:21:15
【问题描述】:

我有如下所示的快速代码:

var jsFriendlyFreinds = [JSObject]()
for friend in friends {
  let jsFriend = await FriendsPlugin.createFriendResult(friend)
  jsFriendlyFreinds.append(jsFriend)
}

我希望所有异步调用同时发生,就像 javascript 的 Promise.all 工作方式一样。对于一次性我知道您可以使用async let,但我不确定如何在循环或映射的上下文中执行此操作。谢谢!

【问题讨论】:

    标签: swift async-await


    【解决方案1】:

    您可以在 for 语句中使用 async 关键字,例如

    for friend in friends async throws {
    

    【讨论】:

    • 但是,这会使循环异步吗?我的印象是,对于循环的每次迭代,该语法仍然会阻塞。
    • 我不熟悉这种模式。当然,for-await-in 用于异步序列,但不是这种模式。你确定吗?你能提供一些参考吗?
    【解决方案2】:

    你可以试试这个方法,使用await withTaskGroup(...), 如本示例代码所示,进行所有异步调用 并行发生。

    let friends: [String] = ["friend-1","friend-2","friend-3"]
    var jsFriendlyFreinds = [GoodFriend]()
    
    func getFriends() async {
        // get all friends in parallel
        return await withTaskGroup(of: GoodFriend.self) { group -> Void in
            for friend in friends {
                group.addTask { await createFriendResult(friend) }
            }
            for await value in group {
                jsFriendlyFreinds.append(value)
            }
        }
    }
    
    // for testing
    func createFriendResult(_ friend: String) async -> GoodFriend {
        // ....
        return GoodFriend(name: "xxx")
    }
    
    struct GoodFriend {
       var name: String
       // ....
    }
    

    像这样使用它:

    await getFriends()
    

    【讨论】:

      【解决方案3】:

      并行循环的基本思想是使用withTaskGroup,然后为每个异步调用使用addTask。然而,诀窍在于,人们通常希望能够将响应与原始数组中的项目相关联(因为在并发运行时,您无法保证收到响应的顺序)。因此,您可以使用带有结果的字典。例如,如果您的 Friend 对象是 Identifiable,您可以这样做:

      func objects(for friends: [Friend]) async -> [Friend.ID: JSObject] {
          await withTaskGroup(of: (Friend.ID, JSObject).self) { group in
              for friend in friends {
                  group.addTask { await (friend.id, FriendsPlugin.createFriendResult(friend)) }
              }
      
              // build dictionary from array of tuples
      
              var dictionary: [Friend.ID: JSObject] = [:]
              while let (index, object) = await group.next() {
                  dictionary[index] = object
              }
      
              // now return array of objects in the order that the friends appeared in the original array
      
              return dictionary
          }
      }
      

      或者,更简洁地说:

      func objects(for friends: [Friend]) async -> [Friend.ID: JSObject] {
          await withTaskGroup(of: (Friend.ID, JSObject).self) { group in
              for friend in friends {
                  group.addTask { await (friend.id, FriendsPlugin.createFriendResult(friend)) }
              }
      
              return await group.reduce(into: [:]) { $0[$1.0] = $1.1 }
          }
      }
      

      或者,如果您只是想要一个 [JSObject] 数组:

      func objects(for friends: [Friend]) async -> [JSObject] {
          await withTaskGroup(of: (Int, JSObject).self) { group in
              for (index, friend) in friends.enumerated() {
                  group.addTask { await (index, FriendsPlugin.createFriendResult(friend)) }
              }
      
              // build dictionary from array of tuples
      
              var dictionary: [Int: JSObject] = [:]
              while let (index, object) = await group.next() {
                  dictionary[index] = object
              }
      
              // now return array of objects in the order that the friends appeared in the original array
      
              return friends.indices.compactMap { dictionary[$0] }
          }
      }
      

      或者,再一次,更简洁:

      func objects(for friends: [Friend]) async -> [JSObject] {
          await withTaskGroup(of: (Int, JSObject).self) { group in
              for (index, friend) in friends.enumerated() {
                  group.addTask { await (index, FriendsPlugin.createFriendResult(friend)) }
              }
      
              let dictionary: [Int: JSObject] = await group.reduce(into: [:]) { $0[$1.0] = $1.1 }
              return friends.indices.compactMap { dictionary[$0] }
          }
      }
      

      主题有很多变化,但想法是使用withTaskGroup/addTask 同时运行请求,然后将结果整理成某种结构,您可以通过该结构将响应与原始数组中的项目相关联。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2023-03-02
        • 1970-01-01
        • 2015-06-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多