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.
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.
如果结果的顺序在这里无关紧要,请改用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).
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.
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
. 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):
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).
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.