RxJS-如何在操作后访问BehaviorSubject的值

时间:2019-02-05 20:06:59

标签: javascript rxjs rxjs6

说我有一个行为主题,通过一个或两个操作来传递:

const inputNumber = new BehaviorSubject(2);

// The current number can be retrieved with `inputNumber.getValue()`

const numberAfterOperations = inputNumber.pipe(
  filter(a => (a+1) % 2), // filter out odd numbers
  map(a => a * a), // square it
);

如何获取numberAfterOperations的值?

// Does not work because `getValue` is not defined!
numberAfterOperations.getValue(); // I expect this to be 4

inputNumber.next(4);

// Does not work!
numberAfterOperations.getValue(); // I expect this to be 16
  

注意:这里的可观察订阅工作正常。我在问同步访问.value(或.getValue()

我在这里注意到了一个类似的问题,但是没有看到解决方法: https://github.com/ReactiveX/rxjs/issues/2378

4 个答案:

答案 0 :(得分:1)

我认为您对可观察对象/主题的工作方式感到困惑。在您显示的示例中,主题不保留4或16的值,这些值是派生的通过对结果可观察对象应用管道操作来实现的。您必须使用first()take(1)之类的运算符才能在对主题的next调用之间获得值。主题仅知道通过next通过它发送的值,不知道可能附加到其上的可观察对象决定如何修改该值。

答案 1 :(得分:0)

Observable.pipe()返回Observable,因此,如果您想访问实际的BehaviourSubject,也必须传递它。

const inputNumber = new BehaviorSubject(2);

// The current number can be retrieved with `inputNumber.getValue()`

const numberAfterOperations = inputNumber.pipe(
  filter(a => (a+1) % 2), // filter out odd numbers
  map(a => a * a), // square it
  map(n => [n, inputNumber])
);

// ...later

numberAfterOperations.subscribe(([number, subject]) => {
  sbject.next(n/2);
});

但是不应该这样做,您可以清楚地看到反模式。将.next设置为单独的方法,然后在需要时将其导入。

答案 2 :(得分:0)

Pipe设置了一个新的可观察对象,该对象在被订阅之前不会执行任何操作。只能使用getValue访问值不是行为主题。您必须订阅它,否则它是冷的可观察到的,根本没有数据顺流而下。将console.log放入地图函数中,您会发现如果没有订阅,它甚至都不会被调用。

如果您可以100%确保订阅将同步运行(就像从BehaviorSubject派生的那样),则可以编写一个取消包装函数,但是如果传递异步可观察的结果,则结果将是不可预测的,很可能返回未定义的结果。

const { BehaviorSubject } = rxjs;
const { filter, map } = rxjs.operators;

const inputNumber$ = new BehaviorSubject(2);

const numberAfterOperations$ = inputNumber$.pipe(
  filter(a => (a+1) % 2), // filter out odd numbers
  map(a => { console.log('mapping'); return a * a; }), // square it
);

inputNumber$.next(4);

console.log('No mapping has happened yet');

console.log(unwrap(numberAfterOperations$));

function unwrap(obs$) {
  let value;
  const sub = obs$.subscribe(val => { value = val; });
  sub.unsubscribe();
  return value;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/6.4.0/rxjs.umd.min.js"></script>

如果您使用的是Angular,则可以在视图中使用异步管道

{{ numberAfterOperations | async }}

答案 3 :(得分:0)

谢谢大家的回答。您已经帮助我意识到,对于行为主题,没有内置的方法可以完成我想要的事情。我想到了以下几点:

function mapBehaviorSubject(behavior, mapFn) {
  const mappedSubject = new BehaviorSubject(mapFn(behavior.getValue()));
  behavior.subscribe({
    next: value => mappedSubject.next(mapFn(value)),
  });
  return mappedSubject;
}

这使我具有所需的行为:

const inputNumber = new BehaviorSubject(2);

const numberAfterOperations = mapBehaviorSubject(inputNumber, x => x * x);

这使我可以拥有一个持久的主题,并且其映射值可以与getValue()同步使用。