【问题标题】:Reset timeout on event with RxJS使用 RxJS 重置事件超时
【发布时间】:2014-11-08 02:53:52
【问题描述】:

我正在尝试使用 RxJS(带有 JQuery 扩展),我正在尝试解决以下用例:

鉴于我有两个按钮(A 和 B),如果在给定时间范围内单击某个“秘密组合”,我想打印一条消息。例如,“秘密组合”可以是在 5 秒内点击“ABBABA”。如果在 5 秒内未输入该组合,则应显示超时消息。这是我目前拥有的:

var secretCombination = "ABBABA";

var buttonA = $("#button-a").clickAsObservable().map(function () { return "A"; });
var buttonB = $("#button-b").clickAsObservable().map(function () { return "B"; });

var bothButtons = Rx.Observable.merge(buttonA, buttonB);

var outputDiv = $("#output");

bothButtons.do(function (buttonName) {
    outputDiv.append(buttonName);
}).bufferWithTimeOrCount(5000, 6).map(function (combination) {
    return  combination.reduce(function (combination, buttonName) {
        return combination + buttonName;
    }, "");
}).map(function (combination) {
    return combination === secretCombination;
}).subscribe(function (successfulCombination) {
    if (successfulCombination) {
        outputDiv.html("Combination unlocked!");
    } else {
        outputDiv.html("You're not fast enough, try again!");
    }
});

虽然这工作得很好,但这并不是我想要的。在新的时间范围内第一次按下按钮 A 时,我需要重置 bufferWithTimeOrCount。我正在寻找的是,一旦按下秘密组合(ABBABA),我就会想要“组合解锁!”显示(我不想等待时间窗口过期)。

【问题讨论】:

    标签: javascript reactive-programming frp rxjs


    【解决方案1】:

    Throttle 是您想要的延迟响应重置效果的典型运算符。

    以下是您如何结合使用节流阀和扫描来收集在 5 秒静音之前输入的组合:

    var evaluationStream = bothButtons
      .merge(bothButtons.throttle(5000).map(function(){return "reset";})) // (2) and (3)
      .scan(function(acc, x) { // (1)
        if (x === "reset") return "";
        var newAcc = acc + x;
        if (newAcc.length > secretCombination.length) {
          return newAcc.substr(newAcc.length - secretCombination.length);
        }
        else {
          return newAcc;
        }
      })
      .map(function(combination) {
        return combination === secretCombination;  
      });
    
    var wrongStream = evaluationStream
      .throttle(5000)
      .filter(function(result) { return result === false; });
    
    var correctStream = evaluationStream
      .filter(function(result) { return result === true; });
    
    wrongStream.subscribe(function() {
      outputDiv.html("Too slow or wrong!");
    });
    
    correctStream.subscribe(function() {
      outputDiv.html("Combination unlocked!");
    });
    

    (1) 我们scan 连接输入字符。 (2) Throttle 等待 5 秒的事件静默,并在该静默之前发出最后一个事件。换句话说,它类似于延迟,除了它会在源 Observable 上看到新事件时重置内部计时器。我们需要重置扫描的串联 (1),因此我们只需将相同的节流 Observable 映射到“重置”标志 (3),扫描将其解释为清除累加器 (acc)。

    这是JSFiddle

    【讨论】:

    • 非常感谢您的详细回答。这是一个很好的改进,但它并不是我想要的(也许问题有点不清楚,我会编辑这个问题)。如果您转到 JSFiddle 页面并单击“BBABBABA”,您会得到“太慢或错误!”。我希望这是正确的,因为您确实单击了 ABBABA(我不在乎在 ABBABA 之前单击了 BB)。我也想“解锁组合!”按下 ABBABA 后立即显示。我不想等到时间窗口到期。
    • 那么为什么需要“5 秒”呢?为什么不直接将扫描中的字符连接起来,然后在下面的映射中取最后 6 个字符进行比较?
    • 因为我仍然希望在启动后 5 秒内输入秘密组合。假设我按了“BB”,现在按“A”。我现在有 5 秒钟的时间来按下剩余的“BBABA”。如果出现超时或错误输入,我必须重新启动。
    • 在这种情况下,您需要将 Observable 一分为二,因为您只希望在一侧进行节流行为。请参阅更新的答案。以及更新后的 JSFiddle:jsfiddle.net/egybp1wk/15
    • jsfiddle jsfiddle.net/egybp1wk/15 将接受正确的输入,只要每个字符在 5 秒内输入,而不是总共在 5 秒内。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-06-11
    • 2021-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-16
    相关资源
    最近更新 更多