पाठ 21 / 25
Cancellation and AsyncSequence
Cancel tasks cooperatively and consume asynchronous streams of values.
Stopping work and handling streams
Cancellation in Swift is cooperative: calling task.cancel() sets a flag, and well-behaved async code checks it with try Task.checkCancellation() (which throws CancellationError) or Task.isCancelled, then stops promptly. Many system APIs, such as URLSession and Task.sleep, respond to cancellation automatically. Structured concurrency propagates cancellation to child tasks, and SwiftUI's .task modifier cancels its task when the view disappears. An AsyncSequence delivers values over time and is consumed with for await: lines of a file, bytes from a network response, notifications or values from URL.lines. AsyncStream bridges callback- or delegate-based APIs into async sequences: you yield values through a continuation and finish when done. For one-shot callback APIs, withCheckedContinuation and withCheckedThrowingContinuation wrap a completion handler into an async function; you must resume each continuation exactly once. Together these tools let you modernise older APIs without rewriting them.
Cancellation checks and an AsyncStream
Long work stops when cancelled; a callback API becomes an async sequence.
func processImages(_ urls: [URL]) async throws -> Int {
var processed = 0
for url in urls {
try Task.checkCancellation() // stop promptly if cancelled
try await resizeAndUpload(url)
processed += 1
}
return processed
}
let job = Task { try await processImages(selectedURLs) }
// later, e.g. when the user taps Cancel:
job.cancel()
func locationUpdates(from provider: LocationProvider) -> AsyncStream<Coordinate> {
AsyncStream { continuation in
provider.onUpdate = { coordinate in continuation.yield(coordinate) }
provider.onStop = { continuation.finish() }
continuation.onTermination = { _ in provider.stop() } // consumer stopped listening
provider.start()
}
}
for await coordinate in locationUpdates(from: provider) {
print(coordinate)
}
func legacyFetch(id: String) async throws -> Data {
try await withCheckedThrowingContinuation { continuation in
oldClient.fetch(id) { result in continuation.resume(with: result) } // resume exactly once
}
}Resume continuations exactly once
Resuming a checked continuation twice crashes, and never resuming it leaves the awaiting task suspended forever. Make sure every code path in the callback resumes it once.
त्वरित जाँच: How does cancellation work in Swift concurrency?
- It forcibly kills the thread
- It restarts the task
- It is cooperative: tasks check for cancellation and stop themselves
- It is not supported
Answer
It is cooperative: tasks check for cancellation and stop themselves — cancel() sets a flag that code observes via checkCancellation or isCancelled.