Ios 仅对某些错误类型重试
我有一个自定义管道,其中我希望对一些可恢复的错误代码进行3次重试尝试,并且我希望为可恢复的错误添加一些短延迟。有人知道我怎么做吗Ios 仅对某些错误类型重试,ios,swift,combine,Ios,Swift,Combine,我有一个自定义管道,其中我希望对一些可恢复的错误代码进行3次重试尝试,并且我希望为可恢复的错误添加一些短延迟。有人知道我怎么做吗 func createRequest(for message: Message) -> AnyPublisher<ResponseMessage, Error> { Future<ResponseMessage, Error> { promise in ..... } .tryCatch({
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()
}
func createRequest(用于message:message)->AnyPublisher{
未来{
.....
}
.tryCatch({error->AnyPublisher-in)
//如果错误是可恢复的错误,请重试,否则直接失败
如果案例让MessageBusError.messageError(responseError)=错误{
如果响应错误是可识别的{
//仅针对可恢复错误进行下一次尝试
抛出错误
}
}
//如果错误代码不可恢复,则应直接失败
返回失败(错误:error)
.删除任何发布者()
})
.重试(3)
.删除任何发布者()
}
通常,我会尽量避免创建新的发布服务器,而更喜欢由内置运营商编写发布服务器。我发现在这里做起来相当棘手。也许有人可以建议一个更好的方法
重试
在任何失败时重新订阅,因此为了欺骗它,我将所有不可恢复的错误打包成一个包含错误的值,但将可恢复的错误作为失败保留给。重试
;然后最终将结果
解压缩回相应的值/错误
以下是它在您的案例中的工作方式:
func createRequest(for message: Message)-> AnyPublisher<ResponseMessage, Error> {
Future<ResponseMessage, Error> { promise in
.....
}
// pack a value into Result
.map { v -> Result<ResponseMessage, Error> in .success(v) }
.tryCatch { error -> AnyPublisher<Result<ResponseMessage, Error>, Error> in
if case let MessageBusError.messageError(responseError) = error {
if responseError.isRecoverable {
// Keep recoverable errors as failures
throw error
}
}
// pack a non-recoverable error into Result with a failure
return Just(.failure(error)).setFailureType(Error.self)
.eraseToAnyPublisher()
}
.retry(3)
// unpack back
.flatMap { result in result.publisher }
.eraseToAnyPublisher()
}
基本上,您需要一个
retryIf
操作符,因此您可以提供一个闭包来告诉Combine哪些错误应该重试,哪些错误不应该重试。我不知道有这样的运营商,但为自己构建一个运营商并不困难
惯用的方法是为操作符扩展Publisher
名称空间,然后扩展Publisher
以添加对该操作符的支持,以便将其与其他操作符链接在一起
实现可以如下所示:
扩展发布程序{
结构重试:发布者{
typealias输出=P.输出
typealias故障=P.故障
让出版商:P
让时间:Int
let条件:(P.失败)->Bool
func receive(订阅服务器:S),其中S:subscriber,Failure==S.Failure,Output==S.Input{
保护时间>0其他{返回发布服务器.receive(订阅服务器:订阅服务器)}
publisher.catch{(错误:P.Failure)->中的任何publisher
如果条件(错误){
返回重试(publisher:publisher,times:times-1,条件:condition)
}否则{
返回失败(错误:error).eraseToAnyPublisher()
}
}.接收(订户:订户)
}
}
}
扩展发布程序{
func重试(次数:Int,如果条件:@escaping(Failure)->Bool)->publisher.retryf{
publisher.retryf(publisher:self,times:times,condition:condition)
}
}
用法:
func createRequest(用于message:message)->AnyPublisher{
推迟{
未来{
//未来代码
}
}
.重试(次数:3){中出现错误
如果case let MessageBusError.messageError(responseError)=error,responseError.isRecoverable{
返回真值
}
返回错误
}
.删除任何发布者()
}
请注意,我将您的未来
包装在一个延迟
内,否则重试
操作符将毫无意义,因为闭包不会执行多次。有关该行为的更多详细信息,请参见:
或者,您可以这样编写
Publisher
扩展:
扩展发布程序{
func retry(u次:Int,如果条件:@escaping(Failure)->Bool)->publisher.retryf{
publisher.retryf(publisher:self,times:times,condition:condition)
}
func retry(u次:Int,除非条件:@escaping(Failure)->Bool)->publisher.retryf{
重试(次数,如果:{!条件($0)})
}
}
,它支持一些时髦的东西,比如:
扩展错误{
变量可恢复:Bool{…}
变量不可恢复:Bool{…}
}
//收到可恢复错误时,最多重试3次
//当第一次遇到一个错误时,即
//不可恢复
某出版商
.重试(3,如果:\.isRecoverable)
//最多重试3次,第一次退出时
//遇到无法恢复的错误
某出版商
.重试(3,除非:\.不可恢复)
或者更时髦的红宝石风格:
extension Int{
变量时间:Int{self}
}
某出版商
.重试(3次,除非:\.不可恢复)
我觉得这是更好的方法。非常感谢。不要只使用发布者和setFailureType
操作符,而是使用Result.publisher
。您可以为后者指定故障类型。@PeterSchorn,我不确定是否遵循。这是怎么回事?用Result.Publisher(.success(.failure(err)))替换Just(.failure(err)).setFailureType(to:U.failure.self)
)
@PeterSchorn,啊,我明白了。是的,那也行。但是为什么你认为它比稍微短一点更好呢?也许我错过了什么?老实说,这没什么区别。
extension Publisher {
private func retryOnly<U: Publisher>(
upstream: U,
retries: Int,
when predicate: @escaping (U.Failure) -> Bool
) -> AnyPublisher<U.Output, U.Failure> {
upstream
.map { v -> Result<U.Output, U.Failure> in .success(v) }
.catch { err -> AnyPublisher<Result<U.Output, U.Failure>, U.Failure> in
if predicate(err) {
return Fail(error: err).eraseToAnyPublisher()
} else {
return Just(.failure(err))
.setFailureType(to: U.Failure.self)
.eraseToAnyPublisher()
}
}
.retry(retries)
.flatMap { result in result.publisher }
.eraseToAnyPublisher()
}
func retry(_ retries: Int, when predicate: @escaping (Failure) -> Bool)
-> AnyPublisher<Output, Failure> {
return retryOnly(upstream: self, retries: retries, when: predicate)
}
}
failingPublisher.retry(3, when: { $0 is RecoverableError })