RxScala中take()调用后doOnCompleted不触发的问题排查
RxScala里take(n)和doOnCompleted()顺序踩坑?看这里!
嘿,我帮你把这个问题掰明白!你遇到的异常行为其实是Rx操作符链式调用顺序导致的,这可是Rx新手常踩的小坑之一~
为啥两种写法结果不一样?
先给你拆解两种调用顺序的本质:
第一种:
obs.take(2).doOnCompleted(...)
这里take(2)先工作——它会在拿到2个元素后,主动给下游发送onCompleted事件,然后切断和上游obs的订阅。而后面的doOnCompleted是绑定在take(2)返回的Observable上的,所以它能捕获到这个take(2)触发的完成事件,自然所有日志都正常输出。第二种:
obs.doOnCompleted(...).take(2)
这次doOnCompleted是绑定在原始obs上的,它等着原始obs发送onCompleted事件。但take(2)在拿到2个元素后,直接给下游发完成,同时立刻取消了对上游(也就是绑了doOnCompleted的那个Observable)的订阅。这就导致原始obs后续的事件(包括它自己的onCompleted)根本传不到doOnCompleted那里,所以你永远看不到它触发。
怎么解决?
核心就是把doOnCompleted放在take(2)之后,让它监听take(2)触发的完成事件。给你写个修正后的代码示例:
import rx.lang.scala.Observable object Tester extends App { // 先搞个测试用的Observable,比如发射1到5的整数 val obs = Observable.from(1 to 5) println("=== 正常工作的订阅 ===") obs.take(2) .doOnCompleted(() => println("✅ doOnCompleted 触发啦!")) .subscribe( num => println(s"📥 收到元素: $num"), err => println(s"❌ 出错了: ${err.getMessage}"), () => println("🎉 订阅完成") ) // 如果之前的写法是把doOnCompleted放前面,改成上面这样就ok了 }
敲黑板划重点
- Rx的操作符链式调用是从左到右依次处理事件流,每个操作符都会返回一个新的Observable,顺序变了,逻辑完全不一样。
take(n)的特性是:拿到n个元素后立刻发完成,同时切断上游订阅——上游剩下的所有事件(包括上游自己的完成)都不会再往下传。doOnCompleted只认它直接挂载的那个Observable的完成事件,所以位置一定要放对!
内容的提问来源于stack exchange,提问作者user499882
相关产品推荐
相关产品推荐

