Swift组合仅针对某些错误类型重试 [英] Swift combine retry only for some error types
问题描述
我有一个自定义管道,我想对可恢复的某些错误代码进行3次重试,此外,我还想为可恢复的错误添加一些短暂的延迟.有人知道我该怎么做吗?
I have a custom pipeline where I want to have 3 retry attempt for some error codes which are recoverable plus I want to add some short delay for the recoverable error. Anyone has an idea how I can do it?
func createRequest(for message: Message) -> AnyPublisher<ResponseMessage, Error> {
Future<ResponseMessage, Error> { promise in
.....
}
.tryCatch({ error -> AnyPublisher<ResponseMessage, Error> in
// If error is a recoverable error retry, otherwise fail directly
if case let MessageBusError.messageError(responseError) = error {
if responseError.isRecoverable {
// Make a next attempt only for recoverable error
throw error
}
}
//Should fail directly if the error code is not recoverable
return Fail<ResponseMessage, Error>(error: error)
.eraseToAnyPublisher()
})
.retry(3)
.eraseToAnyPublisher()
}
推荐答案
基本上,您需要 retryIf
运算符,因此您可以提供一个闭包来告知Combine应该重试哪些错误,而不是重试哪些错误..我不知道这样的运算符,但是为自己构建一个运算符并不难.
Basically, you need a retryIf
operator, so you can provide a closure to tell Combine which errors should be retried, and which not. I'm not aware of such an operator, but it's not hard to build one for yourself.
惯用的方法是为操作员使用新类型扩展 Publishers
命名空间,然后扩展 Publisher
以添加对该操作符的支持,以便您可以将其链接以及其他运营商.
The idiomatic way is to extend the Publishers
namespace with a new type for your operator, and then extend Publisher
to add support for that operator so that yo can chain it along with other operators.
实现可能如下所示:
extension Publishers {
struct RetryIf<P: Publisher>: Publisher {
typealias Output = P.Output
typealias Failure = P.Failure
let publisher: P
let times: Int
let condition: (P.Failure) -> Bool
func receive<S>(subscriber: S) where S : Subscriber, Failure == S.Failure, Output == S.Input {
guard times > 0 else { return publisher.receive(subscriber: subscriber) }
publisher.catch { (error: P.Failure) -> AnyPublisher<Output, Failure> in
if condition(error) {
return RetryIf(publisher: publisher, times: times - 1, condition: condition).eraseToAnyPublisher()
} else {
return Fail(error: error).eraseToAnyPublisher()
}
}.receive(subscriber: subscriber)
}
}
}
extension Publisher {
func retry(times: Int, if condition: @escaping (Failure) -> Bool) -> Publishers.RetryIf<Self> {
Publishers.RetryIf(publisher: self, times: times, condition: condition)
}
}
用法:
func createRequest(for message: Message) -> AnyPublisher<ResponseMessage, Error> {
Deferred {
Future<ResponseMessage, Error> { promise in
// future code
}
}
.retry(times: 3) { error in
if case let MessageBusError.messageError(responseError) = error, responseError.isRecoverable {
return true
}
return false
}
.eraseToAnyPublisher()
}
请注意,我将您的 Future
包装在 Deferred
中,否则, retry
运算符将毫无意义,因为将不执行闭包操作多次.有关此行为的更多详细信息,请参见: Swift.结合.重试时有什么方法可以多次调用发布者块?.
Note that I wrapped your Future
within a Deferred
one, otherwise the retry
operator would be meaningless, as the closure will not be executed multiple times. More details about that behaviour here: Swift. Combine. Is there any way to call a publisher block more than once when retry?.
或者,您可以像这样编写 Publisher
扩展名:
Alternatively, you can write the Publisher
extension like this:
extension Publisher {
func retry(_ times: Int, if condition: @escaping (Failure) -> Bool) -> Publishers.RetryIf<Self> {
Publishers.RetryIf(publisher: self, times: times, condition: condition)
}
func retry(_ times: Int, unless condition: @escaping (Failure) -> Bool) -> Publishers.RetryIf<Self> {
retry(times, if: { !condition($0) })
}
}
,它启用了一些时髦的东西,例如:
, which enables some funky stuff, like this:
extension Error {
var isRecoverable: Bool { ... }
var isUnrecoverable: Bool { ... }
}
// retry at most 3 times while receiving recoverable errors
// bail out the first time when encountering an error that is
// not recoverable
somePublisher
.retry(3, if: \.isRecoverable)
// retry at most 3 times, bail out the first time when
// an unrecoverable the error is encountered
somePublisher
.retry(3, unless: \.isUnrecoverable)
甚至是更时髦的红宝石风格:
Or even funkier, ruby-style:
extension Int {
var times: Int { self }
}
somePublisher
.retry(3.times, unless: \.isUnrecoverable)
这篇关于Swift组合仅针对某些错误类型重试的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!