Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/401.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript RxJS:一个可观察对象,它将自己的旧值作为输入_Javascript_Rxjs_Reactivex - Fatal编程技术网

Javascript RxJS:一个可观察对象,它将自己的旧值作为输入

Javascript RxJS:一个可观察对象,它将自己的旧值作为输入,javascript,rxjs,reactivex,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.opera

我正在尝试实现如下内容:

// 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)
);
这显然是不可编译的胡说八道,因为我们试图在创建
之前对
进行采样,但希望它能明确我的意图:从
a
发出的每个值都应该依赖于
a
先前发出的一个旧值。
a
的哪个旧值取决于
c
最后一次发射东西的时间。这有点像在
a
上成对调用
a
,除了我不想要最后两个值,而是最新的和后面的另一个值

请注意,如果不是针对
startWith(aininitial)
位,这甚至不是一个定义良好的问题,因为
a
发出的第一个值将循环定义,并参考自身。但是,只要第一个值
a
是单独指定的,构造就具有数学意义。我只是不知道如何以干净的方式在代码中实现它。我的直觉是,写一个定制的主题会有一个冗长的方法,但是更优雅的东西会非常受欢迎


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

您可以根据行为主题创建一个名为previous subject的新主题

行为主体的来源在这里

因此,让我们创建一个前一个主题,该主题与前一个值一起发出当前值

import { Subject } from 'rxjs';
import { Subscriber } from 'rxjs';
import { Subscription } from 'rxjs';
import { SubscriptionLike } from 'rxjs';
import { ObjectUnsubscribedError } from 'rxjs';

export class PreviousSubject<T> extends Subject<T[]> {

  _value: T[];

  constructor(value: T, previous?: T) {
    super();
    this._value = [value, previous];
  }

  get value(): T[] {
    return this.getValue();
  }

  /** @deprecated This is an internal implementation detail, do not use. */
  _subscribe(subscriber: Subscriber<T[]>): Subscription {
    const subscription = super._subscribe(subscriber);
    if (subscription && !(<SubscriptionLike>subscription).closed) {
      subscriber.next(this._value);
    }
    return subscription;
  }

  getValue(): T[] {
    if (this.hasError) {
      throw this.thrownError;
    } else if (this.closed) {
      throw new ObjectUnsubscribedError();
    } else {
      return this._value;
    }
  }

  next(value: T): void {
    this._value = [value, this._value[0]];
    super.next(this._value);
  }
}

经过一番思考,下面是我自己的解决方案,以及一个简单的工作示例:

// Setup for MWE.
// Note that c just emits once a second, the actual values of it don't matter.
const c = rxjs.interval(1000).pipe(rxjs.operators.take(2));
const m = rxjs.interval(300).pipe(rxjs.operators.take(9));
const aInitial = -1;

// Initialize a.
const a = new rxjs.BehaviorSubject();
a.next(aInitial);

// For testing, log the values a emits.
a.subscribe((val) => {
  console.log(`a: ${val}`);
});

// Sample a at each emission of c.
const aSampled = a.pipe(
  rxjs.operators.sample(c),
  rxjs.operators.startWith(aInitial),
);

// Every time m emits, get also the latest sampled value of a, and do something
// with the two.
m.pipe(
  rxjs.operators.withLatestFrom(aSampled),
  rxjs.operators.map(([mValue, oldAValue]) => {
    // In place of actually computing something useful, just log the arguments
    // and for the sake of demonstration return their product.
    console.log(`m: ${mValue}, old a at last c-emission: ${oldAValue}`);
    return mValue*oldAValue;
  }),
).subscribe(a);  // Route the output back into a.
输出

a: -1
m: 0, old a at last c-emission: -1
a: 0
m: 1, old a at last c-emission: -1
a: -1
m: 2, old a at last c-emission: -1
a: -2
m: 3, old a at last c-emission: -2
a: -6
m: 4, old a at last c-emission: -2
a: -8
m: 5, old a at last c-emission: -2
a: -10
m: 6, old a at last c-emission: -10
a: -60
m: 7, old a at last c-emission: -10
a: -70
m: 8, old a at last c-emission: -10
a: -80

诀窍是将
a
设置为主题,允许我们首先发出其初始值,然后将其连接到管道,该管道的输入之一是
a
的旧采样值。普通可观察对象不允许这样做(AFAIK),因为您必须在创建时定义它们的所有输入。不过不需要cusom主题类。

如果我理解正确,基本事件流是
mousemove
mousedown

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

因此,如果这是真的,我们可以用以下观察值模拟
mousemove
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]),
);
我们需要的是,当
鼠标向下
发射时,以某种方式手头有可观察的
a
的值。我们怎么能得到这个

假设我们有一个
行为主体
,名为
value\u of_a
,其初始值为
1
,并保存
a
的值。如果我们有这样的可观察性,当
mousedown
像这样发射时,我们可以得到its值

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
    )),
);
有了
m
、mousemove Observable和
last\u relevant\u a
我们就有了所需的所有可观测数据。事实上,我们只需结合他们最新的排放量,就可以得到计算
a
新值所需的所有元素

const a = combineLatest(m, last_relevant_a)
.submit(
   ([mouseVal, old_a_val] => {
      // do something to calculate the new value of a
   }
);
现在唯一要做的就是确保a的
value\u
发出
a
发出的任何值。这可以通过在
a
本身的订阅中调用
a
value\u来实现

把它们缝合在一起,解决方案可能是

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);
    }
)
const c=间隔(1000)。管道(
点击(cVal=>console.log('click',cVal))
);
常数m=间隔(200)。管道(
映射(mVal=>[mVal,mVal]),
);
_a的常数值_=新行为主体(1);
最后一个相关的常数a=c管道(
开关映射(cVal=>管道的值(
以(1)为例,
)),
);
常数a=组合测试(m,最后一次相关测试);
a、 烟斗(
采取(20)
)
.订阅(
val=>{
//用任意逻辑计算a的新值
常量新值=val[0][0]*val[0][1]*val[1];
//a的新值由a的值u发出
a的值。下一步(a的新值);
}
)

这可能也是
expand
操作符的一个用例,但应该对此进行研究。

这与我自己发布的解决方案非常相似,但解释得更好。经核准的!