Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/swift/19.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ios 仅对某些错误类型重试_Ios_Swift_Combine - Fatal编程技术网

Ios 仅对某些错误类型重试

Ios 仅对某些错误类型重试,ios,swift,combine,Ios,Swift,Combine,我有一个自定义管道,其中我希望对一些可恢复的错误代码进行3次重试尝试,并且我希望为可恢复的错误添加一些短延迟。有人知道我怎么做吗 func createRequest(for message: Message) -> AnyPublisher<ResponseMessage, Error> { Future<ResponseMessage, Error> { promise in ..... } .tryCatch({

我有一个自定义管道,其中我希望对一些可恢复的错误代码进行3次重试尝试,并且我希望为可恢复的错误添加一些短延迟。有人知道我怎么做吗

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 })