Last active
April 3, 2023 16:28
-
-
Save bradleymackey/a68b51c79c1616d5e450cae377e501b4 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public extension Task where Success == Never, Failure == Never { | |
/// Blueprint for a task that should be run, but not yet. | |
struct Blueprint<Output> { | |
public var priority: TaskPriority | |
public var operation: @Sendable () async throws -> Output | |
public init( | |
priority: TaskPriority = .medium, | |
operation: @escaping @Sendable () async throws -> Output | |
) { | |
self.priority = priority | |
self.operation = operation | |
} | |
} | |
} | |
public extension Task where Success == Never, Failure == Never { | |
/// Race for the first result by any of the provided tasks. | |
/// | |
/// This will return the first valid result or throw the first thrown error by any task. | |
static func race<Output>(firstResolved tasks: [Blueprint<Output>]) async throws -> Output { | |
assert(!tasks.isEmpty, "You must race at least 1 task.") | |
return try await withThrowingTaskGroup(of: Output.self) { group -> Output in | |
for task in tasks { | |
group.addTask(priority: task.priority) { | |
try await task.operation() | |
} | |
} | |
defer { group.cancelAll() } | |
if let firstToResolve = try await group.next() { | |
return firstToResolve | |
} else { | |
// There will be at least 1 task. | |
fatalError("At least 1 task should be scheduled.") | |
} | |
} | |
} | |
/// Race for the first valid value. | |
/// | |
/// Ignores errors that may be thrown and waits for the first result. | |
/// If all tasks fail, returns `nil`. | |
static func race<Output>(firstValue tasks: [Blueprint<Output>]) async -> Output? { | |
return await withThrowingTaskGroup(of: Output.self) { group -> Output? in | |
for task in tasks { | |
group.addTask(priority: task.priority) { | |
try await task.operation() | |
} | |
} | |
defer { group.cancelAll() } | |
while let nextResult = await group.nextResult() { | |
switch nextResult { | |
case .failure: | |
continue | |
case .success(let result): | |
return result | |
} | |
} | |
// If all the racing tasks error, we will reach this point. | |
return nil | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Oh, got it. Very much appreciated! Thank you!