简体   繁体   English

Swift Async let with loop

[英]Swift Async let with loop

I want to get data in parallel.我想并行获取数据。 I found an example to call API in parallel but I want to store async let variables with loop.我找到了一个并行调用 API 的例子,但我想用循环存储async let variables

Async let example.异步让示例。 However, this example doesn't use a loop.但是,此示例不使用循环。

async let firstPhoto = downloadPhoto(named: photoNames[0])
async let secondPhoto = downloadPhoto(named: photoNames[1])
async let thirdPhoto = downloadPhoto(named: photoNames[2])

let photos = await [firstPhoto, secondPhoto, thirdPhoto]
show(photos)

I want to do something like the following.我想做类似以下的事情。

let items = photoNames.map({ photo in
    async let item = downloadPhoto(named: photo)
    return item
}) 
let photos = await items
show(photos)
let photos = await photoNames.map(downloadPhoto)
public extension Sequence {  
  func map<Transformed>(
    priority: TaskPriority? = nil,
    _ transform: @escaping (Element) async throws -> Transformed
  ) async rethrows -> [Transformed] {
    try await withThrowingTaskGroup(of: Transformed.self) { group in
      for element in self {
        group.addTask(priority: priority) {
          try await transform(element)
        }
      }
      
      return try await .init(group)
    }
  }
}
public extension Array {
  init<AsyncSequence: _Concurrency.AsyncSequence>(_ asyncSequence: AsyncSequence) async rethrows
  where AsyncSequence.Element == Element {
    self = try await asyncSequence.reduce(into: []) { $0.append($1) }
  }
}

CollectionConcurrencyKit offers a variation on this. CollectionConcurrencyKit对此提供了一个变体。

如果结果的顺序在这里无关紧要,请改用TaskGroup

You can use a task group.您可以使用任务组。 See Tasks and Task Groups section of the The Swift Programming Language: Concurrency (which would appear to be where you got your example).请参阅The Swift Programming Language: Concurrency 的Tasks and Task Groups部分(这似乎是您获得示例的地方)。

One can use withTaskGroup(of:returning:body:) to create a task group to run tasks in parallel, but then collate all the results together at the end.可以使用withTaskGroup(of:returning:body:)创建一个任务组来并行运行任务,但最后将所有结果整理在一起。

Eg here is an example that creates child tasks that return a tuple of “name” and ”image”, and例如,这是一个创建子任务的示例,该任务返回“名称”和“图像”的元组,并且

func downloadImages(with names: [String]) async -> [String: UIImage] {
    await withTaskGroup(
        of: (String, UIImage).self,
        returning: [String: UIImage].self
    ) { [self] group in
        for name in names {
            group.addTask { await (name, downloadPhoto(named: name)) }
        }

        var images: [String: UIImage] = [:]

        for await result in group {
            images[result.0] = result.1
        }

        return images
    }
}

Or, more concisely:或者,更简洁地说:

func downloadImages(with names: [String]) async -> [String: UIImage] {
    await withTaskGroup(
        of: (String, UIImage).self,
        returning: [String: UIImage].self
    ) { [self] group in
        for name in names {
            group.addTask { await (name, downloadPhoto(named: name)) }
        }

        return await group.reduce(into: [:]) { $0[$1.0] = $1.1 }
    }
}

They run in parallel:它们并行运行:

在此处输入图片说明

But you can extract them from the dictionary of results:但是您可以从结果字典中提取它们:

let stoodges = ["mo", "larry", "curly"]
let images = await downloadImages(with: stoodges)

imageView1.image = images["mo"]
imageView2.image = images["larry"]
imageView3.image = images["curly"]

For looping thru an array and responding in parallel, you don't use async let .对于遍历数组并并行响应,您使用async let Your imaginary code...你想象中的代码...

let items = photoNames.map({ photo in
    async let item = downloadPhoto(named: photo)
    return item
}) 
let photos = await items
show(photos)

...needs simply to be converted to use a task group (typed directly in browser so be prepared to compensate as needed; note also that you have given insufficient info, as I do not know what type your are getting back from your downloadPhoto , so I have to wing it): ...只需转换为使用任务组(直接在浏览器中输入,因此准备根据需要进行补偿;另请注意,您提供的信息不足,因为我不知道您从downloadPhoto返回的是什么类型,所以我必须振作起来):

var result = [YourPhotoType]()
await withTaskGroup(of:YourPhotoType.self) { group in
    photoNames.forEach { name in
        group.addTask {
            async downloadPhoto(named: name)
        }
    }
    for await photo in group {
        result.append(photo)
    }
}

See my tutorial https://www.biteinteractive.com/swift-5-5-asynchronous-looping-with-async-await/ for more info (esp. on the problem that you've now lost the order of the original array).有关更多信息,请参阅我的教程https://www.biteinteractive.com/swift-5-5-asynchronous-looping-with-async-await/ (特别是关于您现在丢失原始数组顺序的问题) )。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM