RxJS:一个将自己的旧值作为输入的Observable

use*_*051 5 javascript rxjs reactivex

我正在尝试实现这样的事情:

// This obviously doesn't work, because we try to refer to `a` before it exists.
// c and m are some Observables.
const aSampled = a.pipe(
  rxjs.operators.sample(c),
  rxjs.operators.startWith(aInitial)
);
const a = m.pipe(
  rxjs.operators.withLatestFrom(aSampled),
  map(([mValue, oldAValue]) => {
    // Do something.
  }),
  rxjs.operators.startWith(aInitial)
);
Run Code Online (Sandbox Code Playgroud)

现在这显然是无法编译的废话,因为我们sample a在创建它之前就尝试这样做,但是希望它使我的意图很明确:每个发出的值a都应依赖于之前发出的旧值之一a。的哪个旧值a由上次c发出某种东西的时间决定。这有点像调用pairwise上a,但我不希望最后两个值,而是从更靠后的最新和另一个。

请注意,如果不是startWith(aInitial)位,这甚至不是一个定义明确的问题,因为by发出的第一个值a将循环引用其自身来定义。但是,只要a分别指定第一个值,该构造就具有数学意义。我只是不知道如何以干净的方式在代码中实现它。我的直觉是,通过编写某种自定义主题可以有一种long回的方式,但是更优雅的做法将是非常受欢迎的。

为了使这一点更加具体,在我的用例中,我处理的是UI元素的单击并拖动到平移类型。m是mousemove事件的可观察性,c是事件的可观察性mousedown。a然后,它将根据光标所在的位置以及a单击事件发生时的值不断变化。

Pic*_*cci 1

如果我理解正确的话,基本的事件流是mousemove和 a mousedown。

基于这些事件流,您必须计算一个新的流a,它以相同的频率发出mousemove,但其数据是基于鼠标当前位置和a上次mousedown发出时的值进行某些计算的结果。

因此,如果这是真的,我们可以使用以下 Observablesmousemove进行模拟mousedown

// the mouse is clicked every 1 second
const c = interval(1000).pipe(
    tap(cVal => console.log('click', cVal))
);
// the mouse moves diagonally and emits every 200 ms
const m = interval(200).pipe(
    map(mVal => [mVal, mVal]),
);
Run Code Online (Sandbox Code Playgroud)

a我们需要的是在发出时以某种方式掌握 Observable 的值mousedown。我们怎样才能得到这个?

假设我们有一个BehaviourSubject被调用者value_of_a,其初始值为1且保存值a。mousedown如果我们有这样的 Observable,我们可以像这样简单地在发出时获取它的值

const last_relevant_a = c.pipe(       // when mousedown emits
    switchMap(() => value_of_a.pipe(  // the control is switched to the Observable value_of_a
        take(1),                      // and we consider only its first value
    )),
);
Run Code Online (Sandbox Code Playgroud)

有了mmousemove Observable,last_relevant_a我们就拥有了我们需要的所有 Observable。事实上,我们只需将它们的最新排放量结合起来即可获得计算 的新值所需的所有元素a。

const a = combineLatest(m, last_relevant_a)
.submit(
   ([mouseVal, old_a_val] => {
      // do something to calculate the new value of a
   }
);
Run Code Online (Sandbox Code Playgroud)

现在唯一要做的就是确保value_of_a发出 发出的任何值a。这可以通过在其自身的订阅内next调用来完成。value_of_aa

将它们缝合在一起,解决方案可能是这样的

const c = interval(1000).pipe(
    tap(cVal => console.log('click', cVal))
);

const m = interval(200).pipe(
    map(mVal => [mVal, mVal]),
);

const value_of_a = new BehaviorSubject<number>(1);

const last_relevant_a = c.pipe(
    switchMap(cVal => value_of_a.pipe(
        take(1),
    )),
);

const a = combineLatest(m, last_relevant_a);

a.pipe(
    take(20)
)
.subscribe(
    val => {
        // new value of a calculated with an arbitrary logic
        const new_value_of_a = val[0][0] * val[0][1] * val[1];
        // the new value of a is emitted by value_of_a
        value_of_a.next(new_value_of_a);
    }
)
Run Code Online (Sandbox Code Playgroud)

也许这也是expand操作员的一个用例,但应该进行调查。