Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save hoc081098/55c1789821b6eef45f835c79d47140d1 to your computer and use it in GitHub Desktop.
Save hoc081098/55c1789821b6eef45f835c79d47140d1 to your computer and use it in GitHub Desktop.
Demo how to use withTaskCancellationHandler and withCheckedThrowingContinuation to convert the callback style function to async/await function
private class DemoError: Error { }
private final class GetDataRequest: @unchecked Sendable {
private let lock = NSLock()
private var item: DispatchWorkItem?
private var onCancel: (@Sendable () -> Void)?
func start(
block: @Sendable @escaping (Result<Int, Error>) -> Void,
onCancel: @Sendable @escaping () -> Void
) {
self.lock.lock()
defer { self.lock.unlock() }
print("Started")
self.item = .init {
block(
Bool.random()
? .success(.random(in: 0...1_000))
: .failure(DemoError())
)
}
self.onCancel = onCancel
DispatchQueue
.global(qos: .userInitiated)
.asyncAfter(deadline: .now() + 2, execute: self.item!)
}
func cancel() {
self.lock.lock()
defer { self.lock.unlock() }
item?.cancel()
item = nil
onCancel?()
onCancel = nil
print("Cancelled")
}
}
func getData() async throws -> Int {
let req = GetDataRequest()
return try await withTaskCancellationHandler(
operation: {
// This check is necessary in case this code runs after the task was cancelled.
// In which case we want to bail right away.
try Task.checkCancellation()
return try await withCheckedThrowingContinuation { cont in
req.start(
block: {
// This check is necessary in case this code runs after the task was cancelled.
// In which case we want to bail right away.
guard !Task.isCancelled else { return cont.resume(throwing: CancellationError()) }
cont.resume(with: $0)
},
onCancel: {
cont.resume(throwing: CancellationError())
}
)
}
},
onCancel: { req.cancel() }
)
}
func use() {
let task = Task.detached {
do { print("Result:", try await getData()) }
catch { print("Error:", error) }
}
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
print("Start cancelling")
task.cancel()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment