SE-0300 proposed the following for wrapping cancellable completion handler-based functions into async functions:
func download(url: URL) async throws -> Data? {
var urlSessionTask: URLSessionTask?
return try await withTaskCancellationHandler {
urlSessionTask?.cancel()
} operation: {
let result: Data? = try await withUnsafeThrowingContinuation { continuation in
urlSessionTask = URLSession.shared.dataTask(with: url) { data, _, error in
if let error = error {
continuation.resume(throwing: error)
} else {
continuation.resume(returning: data)
}
}
urlSessionTask?.resume()
}
if let result = result {
return result
} else {
return nil
}
}
}
However, that no longer works, as the compiler gives the following error:
Reference to captured var 'urlSessionTask' in concurrently-executing code
What is the up-to-date best practice on wrapping such code in a way that cancellations are properly propagated to underlying tasks?
The original pitch had a really nice suggestion for the API, but it doesn't seem like anything like that exists.
AsyncStream has a Continuation type where one can register onTermination
, but the continuation given by withChecked|Unsafe(Throwing)Continuation
does not have it.