RXjs:如何在流上创建运算符:扫描运算符,其中累加器状态可以通过observable重置

时间:2015-09-08 18:00:39

标签: rxjs

我需要在具有以下特征的流上创建一个新的实例运算符

  • 特征 Rx.Observable.prototype.scan_with_reset(accumulator, seed$)

    其中:

    参数

    accumulator (Function):要在每个元素上调用累加器函数。

    seed$ (Observable):一个observable,其值将用于重启累加器函数。累加器函数具有以下签名function accumulator_fn(accumulator_state, source_value)。我希望seed$中的值将accumulator_state重置为seed值并发出seed值。

    返回 (Observable):由comonadic绑定操作产生的可观察序列(无论这意味着什么,我在这里复制Rxjs文档)。比。正常的scan运算符,这里发生的是当累加器函数重新启动时#39;从seed$ observable发出的种子值中,发出种子值,scan_with_reset运算符发出的下一个值为accumulator_fn(seed, source_value)

  • 使用示例:

    var seed$ = Rx.Observable.fromEvent(document, 'keydown') .map(function(ev){return ev.keyCode}) .startWith(0); var result$ = counter$.scan_with_reset(seed$, function accumulator_fn (acc, counter) {return acc+counter});

    以下图表应详细说明预期结果: seed : 0---------13--------27------------ counter : -1--5--2----6---2-----4---1---3--- result : 0-1--6--8-13-19--21-27-31--32--35-

我最初尝试这样做是为了修改accumulator_fn以使seed$修改一个在accumulator_fn范围内的变量,这样我就可以检测到函数本身的变化。

我在这里追求两个目标:

  • 有一个尽可能无状态且无关闭的实现
  • 了解定义自己的运营商背后的机制 流,这可能是一个简单的例子

我查看了scan源代码:https://github.com/Reactive-Extensions/RxJS/blob/master/src/core/linq/observable/scan.js 但我不知道从那里去哪里。

有没有人有创建Rxjs流操作符的经验?要遵循哪些惯例和陷阱?我可以看一下定制运营商的例子吗?你会如何实现这个特定的?

[更新]:接受答案的一些测试代码

 var seed$ = Rx.Observable.fromEvent(document, 'keydown')
                          .map(function(ev){return ev.keyCode})
                          .startWith(0);
 var counter$ = Rx.Observable.fromEvent(document, 'mousemove')
                             .map(function(ev){return 1});
 var result$ = counter$.scanWithReset(seed$,
                                      function accumulator_fn (acc, counter) {return acc+counter});
 var s = function (x) {console.log("value: ", x)};
 var disposable = result$.subscribe(s)

移动鼠标应该显示一个值增加1,按一个键应该重新启动计数器并按下该键的值。

1 个答案:

答案 0 :(得分:3)

作为创建运算符的一般情况,通常最简单的方法是使用Observable.create方法,它基本上定义了Observable在订阅时应该如何表现,或者只是包装现有的一组运算符ala { {3}}。

当您进一步了解效果时,还有其他一些注意事项(Observable.create在规模上效率不是很高),您可以考虑创建自定义Observable,例如share

对于你的情况,我现在推荐前者。我认为你的问题确实是几个独立的流,我们想要扁平化为单个流。触发重置时,每个新流都将启动。对我来说,这听起来真的很像flatMap

Rx.Observable.prototype.scanWithReset = function ($reset, accum, seed) {
    var source = this;
    //Creates a new Observable
    return Rx.Observable.create(function (observer) {
        //We will be reusing this source so we want to make sure it is shared
        var p = source.publish();


        var r = $reset
            //Make sure the seed is added first
            .startWith(seed)
            //This will switch to a new sequence with the associated value
            //every time $reset fires
            .flatMapLatest(function (resetValue) {
              //Perform the scan with the latest value
              return source.scan(accum, resetValue);
        });

        //Make sure every thing gets cleaned up
        return new Rx.CompositeDisposable(
            r.subscribe(observer),
            //We are ready to start receiving from our source
            p.connect());
    });
}