You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为什么拼接多个FirstAsync会阻塞父Observable完成且仅最后一个onCompleted触发?

链式调用FirstAsync的回调触发逻辑解答

核心原因

Rx操作符的订阅和事件传播遵循从下游到上游逐层订阅、从上游到下游逐层传递事件的规则,而FirstAsync的内置逻辑是:接收到上游的第一个OnNext事件后,会立刻向下游传递该事件,同时主动取消对上游的订阅,并直接向下游发送OnCompleted事件,不会等待上游自身执行完成逻辑。

你的示例执行顺序

我们可以把你给出的链式结构拆成逐层依赖关系:
原始Observable → 第一个FirstAsync → 第一个Do → 第二个FirstAsync → 第二个Do
实际执行的时间线如下:

  • 订阅触发后,原始Observable发射第一个元素
  • 第一个FirstAsync收到元素,向第一个Do传递OnNext事件
  • 第一个Do执行OnNext回调,把元素继续传给第二个FirstAsync
  • 第二个FirstAsync收到第一个元素后,同时触发两个动作:
    • 向第二个Do传递OnNext事件
    • 主动取消对上游(第一个Do)的订阅,同时向第二个Do传递OnCompleted事件
  • 此时第一个FirstAsync本来要向第一个Do传递OnCompleted事件,但因为下游订阅已经被取消,按照Rx协议,上游不会再向已取消订阅的下游发送任何事件,因此第一个Do的OnCompleted回调永远不会被触发。

关于“阻塞父Observable完成”的误解

实际上父Observable并没有被阻塞,只是它的OnCompleted事件的传播路径被下游的FirstAsync主动截断了。如果你的原始Observable本身有完成逻辑,它依然会正常执行自己的完成流程,只是这个完成事件不会传递到被截断的上游回调里。

需触发所有OnCompleted的解决方案

如果需要每个FirstAsync的后续回调都正常触发,可以用多播操作符把原始Observable的事件共享给多个独立的FirstAsync订阅,不要写成链式调用:

// 多播原始Observable的事件
var sharedSource = Observable.XXX().Publish();
// 第一个独立订阅
sharedSource
    .FirstAsync()
    .Do(_ =>
    {

    }, onCompleted: () =>
    {
        // 会正常触发               
    })
    .Subscribe();
// 第二个独立订阅
sharedSource
    .FirstAsync()
    .Do(_ =>
    {

    }, onCompleted: () =>
    {
        // 会正常触发       
    })
    .Subscribe();
// 启动多播
sharedSource.Connect();

内容的提问来源于stack exchange,提问作者Vincent P

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.01 07:09:03