如何实现N个事件均触发至少一次后,响应任一事件并使用最新结果?
看起来你想要实现的是:当某个特定事件E触发时,执行操作,但必须确保所有N个事件都至少触发过一次,并且操作中使用每个事件的最新结果。你一开始用zip的思路可能不太对,因为zip需要每个Observable都发射新值才会触发组合,而不是在单个事件触发时复用其他事件的最新值。
下面给你两种可行的方案,根据你的具体场景选择:
方案1:使用withLatestFrom(适合指定单个触发源的场景)
如果你的触发源是明确的某一个事件E(比如示例中的o2),withLatestFrom是最贴合需求的操作符——它会在源Observable(也就是E)发射新值时,去获取其他所有Observable的最新值,只有当所有其他Observable都至少发射过一次值时,才会触发组合操作。
示例代码:
val o1 = PublishSubject.create<String>() val o2 = PublishSubject.create<String>() val o3 = PublishSubject.create<String>() // 以o2作为触发源,当o2发射时,获取o1、o2、o3的最新值 o2.withLatestFrom(o1, o3) { v2, v1, v3 -> // 组合三个最新值,这里可以替换成你的业务操作 "o1最新值: $v1, o2最新值: $v2, o3最新值: $v3" } .subscribe { result -> println(result) } // 测试场景 o1.onNext("a") // 此时o2没触发,不会执行操作 o3.onNext("c") // 同样o2没触发,不执行 o2.onNext("b") // o2触发,且o1、o3都有值,输出:o1最新值: a, o2最新值: b, o3最新值: c o1.onNext("a2") // o1更新,但o2没触发,不执行 o2.onNext("b2") // o2再次触发,用o1的最新值a2,输出:o1最新值: a2, o2最新值: b2, o3最新值: c
这个方案的优点是简洁直接,完全匹配“只有E触发时才执行,且用所有最新值”的需求,而且自动过滤了所有事件未全部触发的情况(如果o1或o3还没发射过值,o2触发时不会有任何输出)。
方案2:使用combineLatest+过滤(适合任意事件触发,但只在E触发时响应的场景)
如果你需要的是:任意事件触发时,只有当触发的是E,且所有事件都已触发过一次,才执行操作,可以用combineLatest结合过滤逻辑来实现。combineLatest会在任意Observable发射新值时,用所有Observable的最新值组合,但我们可以通过额外的标记来判断这次触发是否来自E。
示例代码:
val o1 = PublishSubject.create<Pair<String, String>>() // Pair<值, 事件标识> val o2 = PublishSubject.create<Pair<String, String>>() val o3 = PublishSubject.create<Pair<String, String>>() Observable.combineLatest(o1, o2, o3) { v1, v2, v3 -> // 记录最新值和本次触发的事件标识 Triple(v1.first, v2.first, v3.first, listOf(v1.second, v2.second, v3.second).last()) } .filter { (_, _, _, trigger) -> // 只保留由o2触发的情况 trigger == "o2" } .subscribe { (v1, v2, v3, _) -> println("o1最新值: $v1, o2最新值: $v2, o3最新值: $v3") } // 测试场景 o1.onNext("a" to "o1") // 触发combineLatest,但过滤掉(不是o2触发) o3.onNext("c" to "o3") // 同样过滤 o2.onNext("b" to "o2") // 触发且通过过滤,输出对应结果 o1.onNext("a2" to "o1") // 触发但过滤 o2.onNext("b2" to "o2") // 触发且通过过滤,用最新值输出
这个方案的灵活性更高,可以扩展到判断任意触发源,但需要给每个事件添加标识,稍微繁琐一点。
为什么zip不适合你的需求?
zip的逻辑是等待所有Observable各发射一个新值,才会执行一次组合。比如如果o1发射了"a",o2发射了"b",o3发射了"c",zip会触发一次;但之后o2再发射"b2",zip不会有任何反应,直到o1和o3也各自发射新值——这显然不符合你“只要E触发就用最新值执行”的需求。
内容的提问来源于stack exchange,提问作者Damn Vegetables

