如何构建一个rx轮询器,在前一个ajax promise解析之后等待一段时间?

时间:2016-03-07 23:47:09

标签: javascript rxjs

正在研究一些方法。基本上,我不想要一个轮询器从轮询开始每30秒开始一次ajax - 我想要一个轮询器在前一个请求返回后30秒启动请求。另外,我想针对失败的指数退避制定一些策略。

这是我到目前为止(Rx4):

rx.Observable.create(function(observer) {
    var nextPoll = function(obs) {
      // the action function invoked below is what i'm passing in
      // to my poller service as any function which returns a promise
      // and must be invoked each loop due to fromPromise caching
      rx.Observable.fromPromise(action())
        .map(function (x){ return x.data; })
        .subscribe(function(d) {       
            // pass promise up to parent observable  
            observer.onNext(d);

            // reset interval in case previous call was an error
            interval = initInterval;   
            setTimeout(function(){ nextPoll(obs); }, interval);
        }, function(e) {
          // push interval higher (exponential backoff)
          interval = interval < maxInterval ? interval * 2 : maxInterval;
          setTimeout(function(){ nextPoll(obs); }, interval);

        });
    };
    nextPoll(observer);
});

在大多数情况下,这就是我想要的。我不喜欢使用setTimeout,但我似乎无法找到更好的Observable方法(除了一次性间隔/定时器与另一个订阅)。

我无法解决的另一件事是能够控制轮询器在最初启动时是否可以立即开始延迟或起火。对于某些用途,我将在开始轮询之前获取数据,因此我可以让它在第一次触发之前等待间隔。到目前为止,我只是在第一个ajax之前或ajax之间发生的定时器/延迟运气,并将它提供给订阅者,这对我来说并不适用。

无论是通常还是在摆脱setTimeout方面,都会感谢任何关于清理它的想法。并且,如果有人有办法以可选的延迟启动此轮询器,那将是巨大的!谢谢大家!!

更新:终于让我的工作方式与我想象的一样。这是什么样的:

function computeInterval(error) {
  if (error) {
    // double until maximum interval on errors
    interval = interval < maxInterval ? interval * 2 : maxInterval;
  } else {
    // anytime the poller succeeds, make sure we've reset to
    // default interval.. this also allows the initInterval to 
    // change while the poller is running
    interval = initInterval;
  }
  return interval;
}

poller$ = rx.Observable.fromPromise(function(){ return _this.action(); })
  .retryWhen(function(errors){
    return errors.scan(function(acc, x) { return acc + x; }, 0)
      .flatMap(function(x){ 
        return rx.Observable.timer(computeInterval(true));
      });
  })
  .repeatWhen(function(notification){
    return notification
      .scan(function(acc, x) { return acc + x; }, 0)
      .flatMap(function(x){ 
        return rx.Observable.timer(computeInterval());
      });
  });

2 个答案:

答案 0 :(得分:2)

只是给了它一些快速思考,所以它必须经过测试,但希望它能让你走上一条有价值的轨道:

var action; // your action function
Rx.Observable.create(function (observer) {
    function executeAction(action) {
        return Rx.Observable.fromPromise(action()).materialize();
    }

    function computeDelay(){
        // put your exponential delaying logic here
    }

    executeAction()
        .expand(function (x) {
            return Rx.Observable.return({})
                .delay(computeDelay())
                .flatMap(function(){return executeAction(action);})
        })
        .subscribe(function(notification){
             if (notification.kind === "N") {
               observer.onNext(notification.value.data);
             } else if (notification.kind === "E") {
               console.log("error:", notification.error.message);
             }
        });
});

简而言之,我们的想法是使用expand运算符进行循环,使用delay运算符进行延迟。看看documentation。 Erros使用materialize运算符和通知机制进行管理(这可以避免在承诺返回错误的情况下突然终止您的轮询流。)

答案 1 :(得分:2)

我使用另一种可能有用和/或可能更简单的技术添加另一个答案。它基于repeatWhen运算符。正如documentation所解释的那样,它允许您重复订阅一个observable,直到您发出重复结束信号表示正常完成或错误。

var action; // your action function
Rx.Observable.create(function (observer) {
    function executeAction(action) {
        return Rx.Observable.fromPromise(action());
    }

    function computeDelay(){
        // put your exponential delaying logic here
    }

executeAction()
  .repeatWhen(function(notification){
    return Rx.Observable.return({}).delay(computeDelay());
  })
  .subscribe(function(x){
    observer.onNext(x.data);
  })
});

这里有terrific article解释repeatWhenretryWhen比官方文档更好(它适用于RxJava但它适用于Rxjs并进行了一些小修改)。它还给出了一个确切想要实现的例子(指数延迟重复)。