Skip to content

Instantly share code, notes, and snippets.

@robertmryan
Created March 14, 2025 17:58
Show Gist options
  • Save robertmryan/654fff6d603bd4c358f3781d2f31b334 to your computer and use it in GitHub Desktop.
Save robertmryan/654fff6d603bd4c358f3781d2f31b334 to your computer and use it in GitHub Desktop.
class ImageManager {
func downloadInOrderIndependentDictionary() async throws -> [String: Image] {
try await withThrowingTaskGroup(of: (String, Image?).self) { group in
let items = await fetchList()
for item in items {
group.addTask {
do {
return try await (item.imageName, self.fetch(imageName: item.imageName))
} catch is CancellationError {
throw CancellationError()
} catch {
return (item.imageName, nil)
}
}
}
return try await group.reduce(into: [:]) { $0[$1.0] = $1.1 }
}
}
func fetchList() async -> [Item] {}
func fetch(imageName: String) async throws -> Image {
try await Task.sleep(for: .seconds(1))
}
}
struct MyAppTests {
@Test func testDownloadTimeout() async throws {
let imageManager = ImageManager()
do {
try await withThrowingTaskGroup(of: Void.self) { group in
group.addTask {
_ = try await imageManager.downloadInOrderIndependentDictionary()
#expect(Bool(false), "The download should have been canceled before finishing")
}
group.addTask {
try await Task.sleep(for: .seconds(0.5))
}
try await group.next()
group.cancelAll()
try await group.waitForAll()
}
#expect(Bool(false), "The above should have thrown an error and we should not get here")
} catch {
#expect(error is CancellationError)
}
}
}
@robertmryan
Copy link
Author

FWIW, the details of the above implementation are of less import than the general idea of just making sure your asynchronous routines throw CancellationError if canceled. Fortunately, most Apple cancelable async API do this already, so you just need to make sure you let those percolate up the call chain. Once you embrace the existing cancelable API, writing tests to detect cancelation are quite simple.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment