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

RxJS- How to access the value of a BehaviorSubject after operations

假设我有一个行为主体,通过一两个操作进行管道传输:

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

Note: the observable subscription here works fine. I'm asking about synchronous access to the .value (or .getValue())

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

我认为您对 Observables/Subjects 的工作方式感到困惑。在您显示的示例中,Subject 不包含 4 或 16 的值,这些值是 derived 通过将管道操作应用于生成的可观察对象。您必须使用像 first()take(1) 这样的运算符才能在对主题的 next 调用之间获取值。 Subject 只知道通过它发送的值 next 它不知道可能附加到它的 observables 如何决定修改该值。

Observable.pipe()returnsObservable,因此如果你想访问实际的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 成为一个单独的方法,并在需要的地方导入它。

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 }}

谢谢大家的回答。你已经帮助我意识到没有内置的方法可以用行为主题做我想做的事情。我总结了以下内容:

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() 同步可用。