【问题标题】:Observable stops emitting when nested error handler calls onExceptionResumeNext当嵌套错误处理程序调用 onExceptionResumeNext 时,Observable 停止发射
【发布时间】:2023-03-11 13:47:01
【问题描述】:

在下面的代码中,我有一个嵌套的 observable。 flatMap 中的 sendMessage 调用 sendMessage 函数,该函数也是一个 observable。如果在这个嵌套的 observable 中发生异常,则假定 onExceptionResumeNext 会捕获异常,处理异常然后继续,就好像什么都没发生一样。异常确实被捕获,但是一旦对异常的处理完成,就不会在流中进行进一步的排放。甚至没有调用 doOnComplete。本质上,onExceptionResume next 只是挂起。

我尝试过 onErrorReturnItem 但结果相同。我没有在 Stackoverflow 或其他地方找到一个例子,它甚至在嵌套的 observable 中显示 onExceptionResumeNext 或 onErrorResumeNext 或 onErrorReturnItem ,经过一天的工作,我怀疑它可能无法支持嵌套的错误处理程序。

注意:在 onExceptionResumeNext 我目前只是返回

Observable.empty<MessageToSend>()

在我的实际代码中,我有处理异常的代码,我尝试返回一个 observable 以及只返回数据。不管我做什么 - 它总是挂起。

fun postMessages() {
    val msgToSendPublisher = BehaviorSubject.createDefault(MessageToSend())

    msgToSendPublisher
        .flatMap { _ ->
            App.context.repository.getMessageToSend().toObservable()
        }
        .doOnError { error ->
            if (error is EmptyResultSetException)
                App.context.repository.setSendStatusToNotSendingForAllMessages()
        }
        .doOnNext { messageToSend ->
            App.context.repository.updateMessage(messageToSend)
        }
        .flatMap { messageToSend ->
            App.context.repository.sendMessage(messageToSend)
        }
        .doOnNext { messageToSend ->
            messageToSend.dateSent = Date()
            App.context.repository.updateDateLastMessageSent(messageToSend)
        }
        .doOnNext { messageToSend ->
            if (messageToSend.totalMessagesToSend == 1)
                App.context.repository.updateSendStatus(messageToSend, MessageSendStates.NOT_SENDING)
            else
                Observable.just(messageToSend)
        }
        .doOnNext {
            msgToSendPublisher.onNext(it)
        }
        .subscribeOn(Schedulers.io())
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(
            { messageToSend ->

            },
            { ex ->
                onMessagesSent()
            },
            {
                onMessagesSent()
            }
        )
}


fun sendMessage(messageToSend: MessageToSend): Observable<MessageToSend> {
    val obs = Observable.fromCallable {
            if (messageToSend.totalMessagesToSend == 3)
                throw Exception("Couldn't send to recipient.")

            messageToSend
        }.map {
            storeMessageSent(messageToSend)
        }.onExceptionResumeNext {
            Observable.empty<MessageToSend>() // Hangs here.
        ).doOnComplete {
            addNewMessageIfRequired(messageToSend, newMessage)
        }

        return obs
}

更新:

我决定测试我发现的使用 onExceptionResumeNext 的示例代码。它看起来像这样:

    Observable.fromArray(1, 2, 3)
        .doOnNext {
            if (it == 2) {
                throw (RuntimeException("Exception on 2"))
            }
        }
        .onExceptionResumeNext(
            Observable.just(10)
        )
        .subscribe(
            {
                var x = it
            },
            {
                var x = it
            },
            {
                var x = 0
                x++
            }
        )

如果您在 onExceptionResumeNext 内的行上放置一个断点,它将在您第一次运行 observable 时被调用,而不仅仅是在抛出异常时。这显然是 RxJava 文档中未标识的行为。任何开发人员都会认为只有在抛出异常时才会调用它。在上面的示例中,将值设置为 10 并不是真正的问题。它实际上只是为发生异常的情况设置返回值。但是,如果这是在数据库中存储内容的更精细的代码(我的应用程序会这样做),那么它会在可观察对象初始化时被调用——这真的很糟糕。尽管有这个发现,它仍然没有解决我的问题,因为没有发出更多的项目。我在示例代码中发现的是,当调用 onExceptionResumeNext 时,也会调用 onComplete。太糟糕了,文档也没有提到。

【问题讨论】:

  • 你把那些onErrorResumeNext放在哪里了?我在提供的代码中没有看到它。把它放在可能失败的内部源上,而不是放在主序列中。
  • 我使用的是onExceptionResumeNext,所以上面的代码没有显示onErrorResumeNext。但如前所述,没关系 - 如果我使用 onErrorResumeNext,问题仍然存在。主流和嵌套流需要自己的错误处理。我只是不完全相信这是可能的。还要记住,完全可以使用包含嵌套 observable 的第三方库,因此应该可以支持嵌套错误处理。
  • 我认为你又遇到了 Kotlin 的歧义之一。你看,在onExceptionResumeNext { } 中,花括号创建了一个ObservableSource.subscribe(Observer) 主体,它什么都不做,并且对你隐藏,因为it 是可选的。用常规大括号替换花括号,看看这是否有助于onExceptionResumeNext
  • 用圆括号替换花括号会导致 onExceptionResumeNext 一直被调用。我的假设是圆括号强制调用方法,而花括号提供匿名回调。

标签: kotlin rx-java2


【解决方案1】:

您可能希望使用defer 来推迟函数调用的执行,这会在调用时产生副作用:

Observable<Integer> createFallback() {
    System.out.println("Why is this executing now?!");
    return Observable.empty();
}

Observable.<Integer>error(new Exception())
.onExceptionResumeNext(createFallback())
.subscribe();

createFallback 运行是因为您指定它通过调用它来运行。如果序列被改写,原因应该会更明显:

Observable<Integer> fallback = createFallback();

Observable.<Integer>error(new Exception())
.onExceptionResumeNext(fallback)
.subscribe();

现在如果你注释掉error-observable部分,它仍然执行createFallback()吗?是的,RxJava 甚至还没有参与到这一点上。

如果您希望这种副作用不会发生在 createFallback 上,则必须推迟整个方法的执行,为此有一个运算符:defer

Observable.<Integer>error(new Exception())
.onExceptionResumeNext(Observable.defer(() -> createFallback()))
.subscribe();

我认为这在 Kotlin 中看起来像这样:

Observable.error(new Exception())
.onExceptionResumeNext(Observable.defer { createFallback() })
.subscribe()

【讨论】:

  • 这有帮助。谢谢。
  • 应该指出,虽然你的解决方案解决了 onExceptionResumeNext 挂起代码的问题,但我注意到的关于 onExceptionResumeNext 终止流的另一个问题仍然存在。 onComplete 仍然会被调用。这很糟糕,因为给人的印象是流通常会继续,好像什么也没发生一样。这显然不是真的。
  • 请提供您遇到问题的代码,不要遗漏重要部分。最好有一个单元测试来证明你的问题。
  • .onExceptionResumeNext {observer -> Observable.just(messageToSend).subscribe(observer) } 这在没有延迟的情况下有效。我一直在寻找的那种。
猜你喜欢
  • 2016-05-31
  • 2023-04-05
  • 1970-01-01
  • 1970-01-01
  • 2018-09-10
  • 2021-04-24
  • 1970-01-01
  • 2013-12-09
  • 1970-01-01
相关资源
最近更新 更多