Angular 2 Http RetryWhen

时间:2016-12-06 21:57:13

标签: angular rxjs observable rxjs5 angular-http

我正在尝试在HTTP调用中使用retryWhen

尝试使用时效果很好:

return this.http.get(`${environment.apiUrl}/track/${this.user.instance._id}/${this.currentPlayer.playlist.id}/next?s=${this.playerCounter}`, options)
      .timeout(500, new TimeoutError(`Timeout trying to get next track. [instanceId=${this.user.instance._id}]`))
      .retryWhen(attempts => {
        return Observable.range(1, 3).zip(attempts, i => i).flatMap(i => 3 === i ? Observable.throw(attempts) : Observable.timer(i * 1000));
      })

如果出现超时错误,最多会尝试3次。

但是,总是有一个buuut,我想让它更抽象地用于各种用例,为此,我必须检查错误的类型。

只会重试TechnicalErros。

所以我尝试了这个没有成功。

.retryWhen(attempts => {
    return attempts.flatMap(error => {
      if(error instanceof TechnicalError) {
        return Observable.range(1, 3).zip(attempts, i => i).flatMap(i => 3 === i ? Observable.throw(attempts) : Observable.timer(i * 1000));
      } else {
        Observable.throw(error);
      }
    });
  })

首次尝试停止,不会执行Observable.timer(),也不执行Observable.throw()

我几乎可以确定问题与第一个flatMap有关,我已经尝试使用mergeMap,但没有成功。

提前致谢!

2 个答案:

答案 0 :(得分:8)

在RxJS 5 flatMap()只是mergeMap()的别名:)。

问题在于您使用retryWhen()运算符的回调方式。它只调用一次,然后每次出现错误信号时,它都会被推送到此回调返回的Observable。

在您的第二个示例中,您将从attempts.flatMap返回Observable,然后再次使用.zip(attempts, i => i)订阅该回调。但是这个zip运算符永远不会被调用,因为在attempts.flatMap已经消耗了该值之后调用它。这也是Observable.range(1, 3)始终从头开始的原因。

我知道这看起来令人困惑。请注意:

  • retryWhen()的回调只调用一次。
  • 每次出现错误时都会调用attempts.flatMap()的回调。

因此,您只需重新构建代码,例如:

var source = Observable.create(obs => {
        obs.next(1);
        obs.next(2);
        obs.error(new TechnicalError('error from source'));
    })
    .retryWhen(attempts => {
        console.log('retryWhen callback');
        let count = 0;

        return attempts.flatMap(error => {
            if (error instanceof TechnicalError) {
                console.log(error);
                return ++count >= 3 ? Observable.throw(error) : Observable.timer(count * 1000);
            } else {
                return Observable.throw(error);
            }
        });
    })
    .subscribe(
        val => console.log(val),
        err => console.log('subscribe error', err),
        _ => console.log('complete')
    );

打印到控制台:

1
2
retryWhen callback
TechnicalError { msg: 'error from source' }
1
2
TechnicalError { msg: 'error from source' }
1
2
TechnicalError { msg: 'error from source' }
subscribe error TechnicalError { msg: 'error from source' }

查看现场演示:https://jsbin.com/hobeda/3/edit?js,console

答案 1 :(得分:0)

我遇到了同样的问题,并找到了一种方法来使用范围运算符来生成重试计数而不是计数器变量,这里是代码:

  var source = Observable.create(obs => {
  obs.next(1);
  obs.next(2);
  obs.error(new TechnicalError('error from source'));
})
  .retryWhen(error => {
    console.log("Error occured, retryWhen initialized");
    return error.zip(Observable.range(1, 4), (error, i) => {
      return {
        ErrorObj: error,
        RetryCount: i
      }
    })
      .map(obj => {
        if (error instanceof TechnicalError) {
          if (obj.RetryCount > 3)
            throw obj.ErrorObj;
          //Retry every one sec for 3 times
          console.log('Retry # ' + obj.RetryCount);
          return Observable.timer(obj.RetryCount * 1000);
        }
        else {
          throw obj.ErrorObj;
        }
      }).concatAll()
  })
  .subscribe(
  val => console.log(val),
  err => console.log('subscribe error', err),
  _ => console.log('complete')
  );

我们的想法是将范围运算符放置在仅初始化一次的位置(retryWhen回调),这样zip运算符会将任何错误与新的范围编号组合在一起,并将其传递给地图运算符以执行错误检查逻辑