首页 > 解决方案 > 如何为单个事件循环滴答重新计算一次可观察的?

问题描述

我有一个用例,我在多个可观察对象之间共享一个可观察对象,最后这 2 个可观察对象合并为一个。

问题是,每当我单击文档时,都会调用两次订阅回调,但我需要调用一次包含最后一个值的回调:

import { fromEvent, of, combineLatest } from "rxjs";
import { map, switchMap, startWith } from "rxjs/operators";

const sharedState$ = fromEvent(document, "click").pipe(
  map((event) => event.clientX),
  startWith(0)
);

const stateA$ = of(5).pipe(
  switchMap((a) => sharedState$.pipe(map((x) => x + a)))
);
const stateB$ = of(7).pipe(
  switchMap((b) => sharedState$.pipe(map((x) => x + b)))
);

const result$ = combineLatest([stateA$, stateB$]).pipe(map(([a, b]) => a + b));

result$.subscribe(console.log);

为了方便起见,这里是codeandbox链接:

从技术上讲,我理解为什么会这样。第一次sharedState$更新被传播到stateA$,因此整个result$被重新计算。接下来同样的事情正在发生stateB$

result$但是这两个更新都是在单个事件循环滴答声中一个接一个地同步发生的,这对于导致单个订阅者的流通知是有意义的。

最后,我想出了debounceTime(0)在流上应用运算符result$,解决了这个问题。但我觉得它比实际解决方案更像是一种解决方法:

// works, but I'm not sure whether it's the right way
const result$ = combineLatest([stateA$, stateB$]).pipe(
  map(([a, b]) => a + b),
  debounceTime(0)
);

处理此类事情的惯用方式是什么?

PS。显然,对于那个特定的用例,它可以以不同的方式解决,这将导致单个订阅者调用,但我的真实用例非常复杂,不幸的是,这些部分被合并到一个动态组合的最终 observable 中,并且可以包含相同的 observable里面。

标签: rxjsobservable

解决方案


这将解决您提到的问题:

import { zip } from "rxjs";
import { distinctUntilChanged } from "rxjs/operators";


const result$ = zip([stateA$, stateB$]).pipe(map(([a, b]) => a + b), distinctUntilChanged());

对于我们需要在任何 observable 发出时触发的情况:

只需在您的初始解决方案中添加一个运算符,这样它就不会在单击同一点时多次发出。

const result$ = combineLatest([stateA$, stateB$]).pipe(
  map(([a, b]) => a + b),
  debounceTime(0),
  distinctUntilChanged()
);

推荐阅读