如何处理 RxSwift 的重试和错误处理最佳实践

5
我阅读了一些帖子,说处理RxSwift的最佳实践是仅将致命错误传递给onError并将结果传递给onNext。这对我很有意义,直到我意识到我不能再处理重试了,因为它只会在onError发生时才会发生。
我如何处理这个问题?
另一个问题是,如何混合处理全局和本地重试?
一个例子是iOS收据验证流程。
1.尝试从本地获取收据
2. 如果失败,请向Apple服务器请求最新收据。
3.将收据发送到我们的后端进行验证。
4.如果成功,则整个流程完成
5.如果失败,请检查错误代码是否可以重试,然后返回1。
在新的1中,它将强制从apple服务器请求新的收据。当再次到达5时,整个流程将停止,因为这已经是第二次尝试了。意味着只重试一次。
所以,在这个例子中,如果不使用rx并且使用状态机,则会使用状态机并共享一些全局状态,例如isSecondAttempt: BoolshouldForceFetchReceipt: Bool等。
我如何在rx中设计此流程? 带有这些在流程中设计的全局共享状态。
1个回答

5

我看到一些帖子说,处理RxSwift的最佳实践是只将致命错误传递给onError,并将结果传递给onNext。

我不同意这种观点。这基本上是在说,只有在程序员犯了错误时才应该使用onError。您应该在不幸的情况下或中止过程时使用错误。它们就像抛出异常一样,只是以异步方式。

以下是您的算法作为Rx链。

enum ReceiptError: Error {
    case noReceipt
    case tooManyAttempts
}

struct Response { 
    // the server response info
}

func getReceiptResonse() -> Observable<Response> {
    return fetchReceiptLocally()
        .catchError { _ in askAppleForReceipt() }
        .flatMapLatest { data in
            sendReceiptToServer(data)
        }
        .retryWhen { error in
            error
                .scan(0) { attempts, error in
                    let max = 1
                    guard attempts < max else { throw ReceiptError.tooManyAttempts }
                    guard isRetryable(error) else { throw error }
                    return attempts + 1
                }
        }
}

这里是上述使用的支持函数:
func fetchReceiptLocally() -> Observable<Data> {
    // return the local receipt data or call `onError`
}

func sendReceiptToServer(_ data: Data) -> Observable<Response> {
    // send the receipt data or `onError` if the server failed to receive or process it correctly.
}

func isRetryable(_ error: Error) -> Bool {
    // is this error the kind that can be retried?
}

func askAppleForReceipt() -> Observable<Data> {
    return Observable.just(Bundle.main.appStoreReceiptURL)
        .map { (url) -> URL in
            guard let url = url else { throw ReceiptError.noReceipt }
            return url
        }
        .observeOn(ConcurrentDispatchQueueScheduler(qos: .userInitiated))
        .map { try Data(contentsOf: $0) }
}

我仍然对解决方案很犹豫,是使用result/materialize还是onError。参考:http://adamborek.com/how-to-handle-errors-in-rxswift/此外,使用materialise也有助于解决这个问题:https://dev59.com/61QK5IYBdhLWcg3wHsVJ你对这些有什么想法? - Tony Lin
3
有时一个链可以成为更大链的子链。尽管如此,您应该将每个链条视为独立的。如果您不希望链条被处理(即中止),那么您需要使用materialize()。但对于这个链条,错误是正确的解决方案。如果更大的链条不想中止,则我可能会通过.flatMap { getReceiptResonse().materialize() }在更大的链条中使用此链条。 - Daniel T.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接