我需要在具有以下特征的流上创建一个新的实例运算符
特征
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,按一个键应该重新启动计数器并按下该键的值。
答案 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());
});
}