RxSwift,分享+重试机制

Posted

技术标签:

【中文标题】RxSwift,分享+重试机制【英文标题】:RxSwift, Share + retry mechanism 【发布时间】:2018-01-17 17:14:16 【问题描述】:

我有一个可以成功或失败的网络请求

我已经将它封装在一个 observable 中。 我有 2 条请求规则

1) 不能同时有超过 1 个请求

-> 我可以使用一个共享操作符

2) 请求成功后,我不想重复相同的操作 再次请求,只返回最新的值

-> 我可以为此使用 shareReplay(1) 运算符

问题出现在请求失败时,shareReplay(1) 只会重放最新的错误,不会再次重新启动请求。

该请求应在下一次订阅时重新开始。

有谁知道我如何将它变成一个 Observable 链?

// scenario 1
let obs: Observable<Int> = request().shareReplay(1)
// outputs a value
obs.subscribe()
// does not start a new request but outputs the same value as before
obs.subscribe() 

// scenario 2 - in case of an error
let obs: Observable<Int> = request().shareReplay(1)
// outputs a error
obs.subscribe() 
// does not start a new request but outputs the same value as before, but in this case i want it to start a new request
obs.subscribe() 

这似乎是在做我想做的事,但它包括将状态保持在可观察的范围之外,有人知道我如何以更 Rx 的方式实现这一点吗?

enum Err: Swift.Error 
    case x


enum Result<T> 
    case value(val: T)
    case error(err: Swift.Error)


func sample() 

    var result: Result<Int>? = nil
    var i = 0

    let intSequence: Observable<Result<Int>> = Observable<Int>.create  observer in

        if let result = result 
            if case .value(let val) = result 
                return Observable<Int>.just(val).subscribe(observer)
            
        
        print("do work")
        delay(1) 
            if i == 0 
                observer.onError(Err.x)
             else 
                observer.onNext(1)
                observer.onCompleted()
            
            i += 1
        
        return Disposables.create 
        
        .map  value -> Result<Int> in Result.value(val: value) 
        .catchError  error -> Observable<Result<Int>> in
            return .just(.error(err: error))
        
        .do(onNext:  result = $0 )
        .share()

    _ = intSequence
        .debug()
        .subscribe()

    delay(2) 
        _ = intSequence
            .debug()
            .subscribe()

        _ = intSequence
            .debug()
            .subscribe()
    

    delay(4) 
        _ = intSequence
            .debug()
            .subscribe()
    



sample()

它只在我们没有缓存任何东西时才产生工作,但我们需要使用副作用来实现所需的输出

【问题讨论】:

【参考方案1】:

如前所述,RxSwift 错误需要被视为致命错误。它们是您的流通常无法从中恢复的错误,而且通常是用户不会面对的错误。

出于这个原因 - 发出 .error.completed 事件的流将立即释放,您将不会再收到任何事件。

有两种方法可以解决这个问题:

    像刚才那样使用 Result 类型 使用.materialize()(如果需要,使用.dematerialize())。这些第一个运算符会将您的Observable&lt;Element&gt; 转换为Observable&lt;Event&lt;Element&gt;&gt;,这意味着您不会发出错误并终止序列,而是会得到一个元素,告诉您这是一个错误事件,但没有任何终止。

您可以在 Adam Borek 的精彩博文中阅读更多关于 RxSwift 中的错误处理的信息:http://adamborek.com/how-to-handle-errors-in-rxswift/

【讨论】:

是的,实际上我的第二个实验有点基于他的博客文章。我唯一怀疑的是“重播”部分。我通过保留一个单独的状态变量来解决这个问题,但感觉这可以改进【参考方案2】:

如果Observable 序列发出错误,它永远不会发出另一个事件。但是,使用flatMap 将容易出错的Observable 包装在另一个Observable 中并在允许它们传播到外部Observable 之前捕获任何错误是相当普遍的做法。例如:

safeObservable
    .flatMap 
        Requestor
            .makeUnsafeObservable()
            .catchErrorJustReturn(0)
    
    .shareReplay(1)
    .subscribe()

【讨论】:

是的,确实感谢您的提示,但是如果您首先遇到错误,catchJustReturn 将被重播,而不是启动新请求

以上是关于RxSwift,分享+重试机制的主要内容,如果未能解决你的问题,请参考以下文章

RxSwift - 使用 maxCount 重试网络请求直到成功

重试延迟 - RxSwift

Alamofire/RxSwift 如何在状态码 401 上自动刷新令牌和重试请求

RxSwift 更新身份验证令牌

Moya rxswift : 刷新令牌并重启请求

Error Handling