重复/重置可观察的

时间:2016-02-19 10:00:06

标签: javascript reactive-programming rxjs

我正在使用rxjs为智能电视上的遥控器创建“频道数量”选择器。我们的想法是,当您输入数字时,您会在屏幕上看到它们,在您输入数字后,用户实际上会被带到该频道。

我使用两个observable来实现这个目标:

  1. 一个“进度”流,它会侦听所有数字输入,并在数字通过扫描操作符输入时发出连接的数字字符串。

  2. 一个“已完成”的流,在没有输入数字的n毫秒之后,将完成最终的数字字符串。 EG:1-2-3 - > “123”。

  3. 以下是我用来尝试解决此问题的代码:

    的channelNumber:

    module.exports = function (numberKeys, source, scheduler) {
        return function (completedDelay) {
            var toNumericString = function (name) {
                    return numberKeys.indexOf(name).toString();
                },
                concat = function (current, numeric) {
                    return current.length === 3 ? current : current + numeric;
                },
                live = createPress(source, scheduler)(numberKeys)
                    .map(toNumericString)
                    .scan(concat, '')
                    .distinctUntilChanged(),
    
                completed = live.flatMapLatest(function (value) {
                    return Rx.Observable.timer(completedDelay, scheduler).map(value);
                }),
                progress = live.takeUntil(completed).repeat();
    
            return {
                progress: progress,
                completed: completed
            };
        };
    };
    

    createPress:

    module.exports = function (source, scheduler) {
        return function (keyName, throttle) {
            return source
                .filter(H.isKeyDownOf(keyName))
                .map(H.toKeyName);
        };
    };
    

    createSource:

    module.exports = function (provider) {
        var createStream = function (event) {
            var filter = function (e) {
                    return provider.hasCode(e.keyCode);
                },
                map = function (e) {
                    return {
                        type: event,
                        name: provider.getName(e.keyCode),
                        code: e.keyCode
                    };
                };
            return Rx.Observable.fromEvent(document, event)
                .filter(filter)
                .map(map);
        };
    
        return Rx.Observable.merge(createStream('keyup'), createStream('keydown'));
    };
    

    有趣的是,上面的代码,在测试条件下(使用Rx.TestScheduler的模拟源和调度程序)按预期工作。但是在生产中,当调度程序根本没有传递而源是createPress(上面)的结果时,进度流只会发送直到完成,然后再也不会发生。就像重复被完全忽略或冗余一样。我不明白为什么。

    我在这里错过了什么吗?

1 个答案:

答案 0 :(得分:1)

您可以使用Window。在这种情况下,我建议WindowWithTime。您还可以执行更多有趣的操作,例如使用Window(windowBoundaries),然后使用Debounce作为边界传递来源。

source
  .windowWithTime(1500)
  .flatMap(ob => ob.reduce((acc, cur) => acc + cur, ""))

此外,由于我们的窗口是封闭的可观察对象,我们可以使用Reduce从窗口累积值并连接我们的数字。

现在,这个变种将在1.5秒后关闭。相反,我们希望在最后一次按键后等待x秒。 Naïve我们可以做source.window(source.debounce(1000))但是现在我们订阅了两次我们的来源,这是我们想要避免的两个原因。首先我们不知道订阅是否有任何副作用,第二我们不知道订阅订阅会收到事件。最后一件事不是问题,因为我们使用的debounce在最后一次按键后已经增加了延迟,但仍需要考虑。

解决方案是publish我们的来源。为了将发布保留在序列中,我们将其包装到observable.create

Rx.Observable.create(observer => {
    var ob = source.publish();
    return new Rx.CompositeDisposable(
      ob.window(ob.debounce(1000))
        .subscribe(observer),
      ob.connect());
}).flatMap(ob => ob.reduce((acc, cur) => acc + cur, ""))

修改:或者像这样使用publish

source.publish(ob => ob.window(ob.debounce(1000)))
    .flatMap(ob => ob.reduce((acc, cur) => acc + cur, ""))