The Swift Concurrency documentation provides the following example for how to await multiple async functions in parallel.
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]
Is there a way to await an arbitrary number of async functions in parallel? I tried the approach shown in the code below, but could not find a way to make it work.
var photoPromises: [PhotoPromise] = []
for name in photoNames {
photoPromises.append(downloadPhoto(named: name))
}
let photos = await photoPromises
Yes, you have the right idea. I found a similar question here:
Try using a when statement instead to await for fulfilled promises:
when(fulfilled: promiseArray).then { results in
// Do something
}.catch { error in
// Handle error
}
Here is the original question but the link referenced there is broken.
import UIKit
import XCTest
struct Downloader
{
static func download(photoURLs: [URL]) async throws -> [UIImage] {
try await withThrowingTaskGroup(of: UIImage.self) { group in
var collected = [UIImage]()
for url in photoURLs {
group.addTask {
let (data, _) = try await URLSession.shared.data(from: url)
return UIImage(data: data)!
}
}
for try await value in group {
collected.append(value)
}
return collected
}
}
}
final class PhotosTests: XCTestCase
{
func testDownloader() async throws {
let images = try await Downloader.download(photoURLs: [
URL(string: "https://placekitten.com/200/300")!,
URL(string: "https://placekitten.com/g/200/300")!
])
XCTAssertNotNil(images[0])
XCTAssertNotNil(images[1])
XCTAssertEqual(images.count, 2)
}
}
Generic map/reduce version
import UIKit
import XCTest
struct MapReduce
{
static func mapReduce<T,U>(inputs: [T], process: @escaping (T) async throws -> U ) async throws -> [U] {
try await withThrowingTaskGroup(of: U.self) { group in
// map
for input in inputs {
group.addTask {
try await process(input)
}
}
// reduce
var collected = [U]()
for try await value in group {
collected.append(value)
}
return collected
}
}
}
final class PhotosTests: XCTestCase
{
func testDownloader() async throws {
let input = [
URL(string: "https://placekitten.com/200/300")!,
URL(string: "https://placekitten.com/g/200/300")!
]
let download: (URL) async throws -> UIImage = { url in
let (data, _) = try await URLSession.shared.data(from: url)
return UIImage(data: data)!
}
let images = try await MapReduce.mapReduce(inputs: input, process: download)
XCTAssertNotNil(images[0])
XCTAssertNotNil(images[1])
XCTAssertEqual(images.count, 2)
}
}