He estado tratando de probar el operador retryWhen en RxSwift y me he encontrado con el Reentrancy Anomaly de reentrada, aquí está el código:
Observable<Int>.create { observer in observer.onNext(1) observer.onNext(2) observer.onNext(3) observer.onNext(4) observer.onError(RequestError.dataError) return Disposables.create() } .retryWhen { error in return error.enumerated().flatMap { (index, error) -> Observable<Int> in let maxRetry = 1 print("index: \(index)") return index < maxRetry ? Observable.timer(1, scheduler: MainScheduler.instance) : Observable.error(RequestError.tooMany) } } .subscribe(onNext: { value in print("This: \(value)") }, onError: { error in print("ERRRRRRR: \(error)") }) .disposed(by: disposeBag)Con el código de arriba da:
This: 1 This: 2 This: 3 This: 4 index: 0 This: 1 This: 2 This: 3 This: 4 index: 1 ⚠️ Reentrancy anomaly was detected. > Debugging: To debug this issue you can set a breakpoint in /Users/tony.lin/Documents/Snippet/MaterialiseTest/Pods/RxSwift/RxSwift/Rx.swift:97 and observe the call stack. > Problem: This behavior is breaking the observable sequence grammar. `next (error | completed)?` This behavior breaks the grammar because there is overlapping between sequence events. Observable sequence is trying to send an event before sending of previous event has finished. > Interpretation: This could mean that there is some kind of unexpected cyclic dependency in your code, or that the system is not behaving in the expected way. > Remedy: If this is the expected behavior this message can be suppressed by adding `.observeOn(MainScheduler.asyncInstance)` or by enqueing sequence events in some other way. ⚠️ Reentrancy anomaly was detected. > Debugging: To debug this issue you can set a breakpoint in /Users/tony.lin/Documents/Snippet/MaterialiseTest/Pods/RxSwift/RxSwift/Rx.swift:97 and observe the call stack. > Problem: This behavior is breaking the observable sequence grammar. `next (error | completed)?` This behavior breaks the grammar because there is overlapping between sequence events. Observable sequence is trying to send an event before sending of previous event has finished. > Interpretation: This could mean that there is some kind of unexpected cyclic dependency in your code, or that the system is not behaving in the expected way. > Remedy: If this is the expected behavior this message can be suppressed by adding `.observeOn(MainScheduler.asyncInstance)` or by enqueing sequence events in some other way. ERRRRRRR: tooManySolo me preguntaba si alguien sabe la causa de este problema.
Como explica el comentario de la consola, esta advertencia se puede suprimir usando .observeOn(MainScheduler.asyncInstance) como en:
Observable<Int>.from([1, 2, 3, 4]).concat(Observable.error(RequestError.dataError)) .observeOn(MainScheduler.asyncInstance) // this is the magic that makes it work. .retryWhen { error in return error.enumerated().flatMap { (index, error) -> Observable<Int> in let maxRetry = 1 print("Index:", index) guard index < maxRetry else { throw RequestError.tooMany } return Observable.timer(1, scheduler: MainScheduler.instance) } } .subscribe(onNext: { value in print("This: \(value)") }, onError: { error in print("ERRRRRRR: \(error)") })Me tomé la libertad de hacer algunos ajustes menores a su código de ejemplo para mostrar una forma alternativa de escribir lo que tiene.
Usted pidió explicar (a) por qué funciona agregar ObserveOn y (b) por qué es necesario.
Lo que .observeOn(MainScheduler.asyncInstance) es enrutar la solicitud a un subproceso alternativo donde el evento puede finalizar y luego emitir el evento nuevamente en el subproceso principal. En otras palabras, es como hacer esto:
.observeOn(backgroundScheduler).observeOn(MainScheduler.instance) Donde backgroundScheduler se define como:
let backgroundScheduler = SerialDispatchQueueScheduler(qos: .default)Al menos eso es lo que entiendo.
En cuanto a por qué es necesario, no puedo decirlo. Es posible que haya encontrado un error en la biblioteca porque el uso de un retraso de 1 segundo funciona bien sin observeOn.