Skip to content

Instantly share code, notes, and snippets.

@robertmryan
Last active June 5, 2024 15:46
Show Gist options
  • Save robertmryan/2cbfeaa369502411d6dbccfc12565736 to your computer and use it in GitHub Desktop.
Save robertmryan/2cbfeaa369502411d6dbccfc12565736 to your computer and use it in GitHub Desktop.
actor SerialTasksIgnoresAnyError<Success: Sendable> {
private var previousTask: Task<Success, Error>?
private let priority: TaskPriority?
init(priority: TaskPriority? = nil) {
self.priority = priority
}
func add(block: @Sendable @escaping () async throws -> Success) async throws -> Success {
let task = Task(priority: priority) { [previousTask] in
let _ = try? await previousTask?.value
return try await block()
}
previousTask = task
return try await withTaskCancellationHandler {
try await task.value
} onCancel: {
task.cancel()
}
}
}
actor SerialTasksStopsOnAnyErrorIncludingCancelation<Success: Sendable> {
private var previousTask: Task<Success, Error>?
private let priority: TaskPriority?
init(priority: TaskPriority? = nil) {
self.priority = priority
}
func add(block: @Sendable @escaping () async throws -> Success) async throws -> Success {
let task = Task(priority: priority) { [previousTask] in
let _ = try await previousTask?.value
return try await block()
}
previousTask = task
return try await withTaskCancellationHandler {
try await task.value
} onCancel: {
task.cancel()
}
}
}
actor SerialTasksStopsOnlyOnCancelation<Success: Sendable> {
private var previousTask: Task<Success, Error>?
private let priority: TaskPriority?
init(priority: TaskPriority? = nil) {
self.priority = priority
}
func add(block: @Sendable @escaping () async throws -> Success) async throws -> Success {
let task = Task(priority: priority) { [previousTask] in
do {
let _ = try await previousTask?.value // assuming this properly throws `CancellationError`
} catch is CancellationError {
throw CancellationError()
} catch {
// ignore other errors
}
return try await block()
}
previousTask = task
return try await withTaskCancellationHandler {
try await task.value
} onCancel: {
task.cancel()
}
}
}
@robertmryan
Copy link
Author

Disclaimer: I have not done exhaustive testing of the above, so caveat emptor. I just wanted to outline a few alternatives and the concerns.

Personally, I now tend to use AsyncChannel when I want to process a series of tasks sequentially. This manual “await prior task” approach was something I used before discovering Swift Async Algorithms library.

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