Created
August 26, 2024 19:14
-
-
Save BrentMifsud/8c13c60342633682159f53f53218015e to your computer and use it in GitHub Desktop.
Retrying Task
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
import Foundation | |
private extension Task where Failure == any Error { | |
static var backoffMultiplier: Int { | |
2 | |
} | |
static func incrementRetryValues(currentRetry: inout Int, currentDelay: inout Int) { | |
currentRetry += 1 | |
currentDelay = currentRetry == 0 ? 100 : currentDelay * Self.backoffMultiplier | |
} | |
} | |
extension Task where Failure == any Error { | |
/// Creates a task that retries itself the specified number of times | |
/// - Parameters: | |
/// - priority: task priority | |
/// - maxRetryCount: the number of retries before failure | |
/// - operation: the task operation | |
/// - shouldRetry: handler to retry on certain errors, defaults to false | |
/// - Returns: Operation result | |
static func retrying( | |
priority: TaskPriority? = nil, | |
maxRetryCount: Int = 5, | |
delayClock: any Clock<Duration> = ContinuousClock(), | |
operation: @escaping @Sendable () async throws -> Success, | |
retryWhile shouldRetry: @escaping (Error) -> Bool = { _ in false } | |
) -> Task<Success, Failure> { | |
Task(priority: priority) { | |
var currentRetry = 0 | |
var currentDelay = 0 | |
var finalError: any Error = _Concurrency.CancellationError() | |
repeat { | |
do { | |
try await delayClock.sleep(for: .milliseconds(currentDelay)) | |
let success = try await operation() | |
return success | |
} catch let networkError as NetworkError { | |
incrementRetryValues(currentRetry: ¤tRetry, currentDelay: ¤tDelay) | |
if currentRetry == maxRetryCount { | |
finalError = networkError | |
} | |
continue | |
} catch let cancellationError as CancellationError { | |
throw cancellationError | |
} catch { | |
if shouldRetry(error) { | |
incrementRetryValues(currentRetry: ¤tRetry, currentDelay: ¤tDelay) | |
if currentRetry == maxRetryCount { | |
finalError = error | |
} | |
continue | |
} else { | |
throw error | |
} | |
} | |
} while currentRetry < maxRetryCount | |
throw finalError | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment