繁体   English   中英

RxJS重试行为不符合预期吗?

[英]RxJS retry not behaving as expected?

我在让RxJS的重试运算符与地图运算符结合使用时遇到一些困难。

本质上,我想做的是获取值流(在本例中为测试目的是GitHub用户),并对每个值对该值进行一些操作。

但是,我希望能够以允许我为该操作设置有限次重试的方式来处理错误。 我还假设RxJS异步处理这种事情。

Rx.Observable.fromPromise(jQuery.getJSON("https://api.github.com/users"))
.concatAll(x => Rx.Observable.of(x))
.map(x => {
    if(x.id % 5 == 0) {
        console.log("error");
        return Rx.Observable.throw("error");
    }
    return x;
})
.retry(3)
.subscribe(x => console.log(x), e => console.log(e));

这是我到目前为止所能获得的,但是它应该重试相同的值3次,只打印一次“错误”,这不是我想要的行为。

我是否错过了一些琐碎的事情,或者如果用RxJS无法做到这种事情呢? 简而言之,我想异步地对值执行操作,将所述操作重新命名有限次,但允许其他有效操作并行继续。 我仅使用promise实现了类似的功能,但是代码相当混乱,我希望RxJS能让我做的事情更好看。

我看到一些可能会导致您出现问题的问题:

1)在map语句中,您通过返回一个observable进行抛出。 如果成功案例还返回了一个您随后将其展平的可观察对象(使用concat,switch或merge),这将是适当的,但是您的成功案例只会发出一个值,而不会展平。 因此,当返回Throw observable时,它永远不会被展平,因此仅被传递并记录了observable对象。

相反,您可以执行简单的javascript throw 'error' (尽管考虑抛出错误对象而不是字符串)

2)当重试出现错误时,它将重新订阅。 如果您使用Observables进行获取,则意味着它将再次进行获取。 但是,您使用承诺进行提取,然后仅根据该承诺创建了可观察对象。 因此,当您重新订阅可观察对象时,它将最终使用与第一次相同的promise对象,并且该promise已经处于已解决状态。 不会进行其他提取。

要解决此问题,您需要具有可观察的版本来进行提取。 如果您需要从头开始制作,可能看起来像这样:

Rx.Observable.create(observer => {
  jQuery.getJSON("https://api.github.com/users")
    .then(result => {
      observer.next(result);
      observer.complete();
    }, err => {
      observer.error(err);
      observer.complete();
    });
})

尽管如果您打算经常这样做,我建议您创建一个为您完成该包装的函数。

编辑:

我的最终目标是实质上能够获取api URL数组,并使用每个URL发起get请求。 如果获取请求由于任何原因而失败,则应尝试总共进行3次以再次到达该端点。 但是,此类get请求的失败不应阻止其他请求的发生,它们应并行发生。 此外,如果get请求失败3次,则应仅将其标记为失败,并且不应从整体上停止该过程。 到目前为止,我还没有找到使用RxJS的方法。

然后我会做这样的事情:

function getJson (url) {
  return Rx.Observable.create(observer) => {
    jQuery.getJSON(url)
      .then(result => {
        observer.next(result);
        observer.complete();
      }, err => {
        observer.error(err);
        observer.complete();
      });
  }
}

const endpoints = ['someUrl', 'someOtherUrl'];
const observables = endpoints.map(endpoint => 
  return getJson(url)
    .retry(3)
    .catch(err => Rx.Observable.of('error'));
});

Rx.Observable.forkJoin(...observables)
  .subscribe(resultArray => {
    // do whatever you need to with the results. If any of them
    //   errored, they will be represented by just the string 'error'
  });

您的地图函数返回Rx.Observable.throw("error"); 这是错误的,因为下面的retry()会随后将其包装(例如,以Observable<Observable<T>>而不是Observable<T>进行包装。您可以使用flatMap进行更正,但还需要通过Rx.Observable.of(x)

.flatMap(x => {
    if(x % 5 == 0) {
        console.log("error");
        return Rx.Observable.throw("error");
    }
    return Rx.Observable.of(x);
})
.retry(3)
.subscribe(x => console.log(x), e => console.log(e));

另一种选择是throw new Error()而不是尝试return Rx.Observable.throw("error");


确保重试正确的事情

Rx.Observable.fromPromise(jQuery.getJSON("https://api.github.com/users"))
  .retry(3)
  .concatAll(x => Rx.Observable.of(x))
  ...

编辑-为清楚起见,删除了较早失败的解决方案(某些评论可能不再相关)

要重试单个失败,您需要中断单个请求并重试那些请求。

为了正确处理错误,请针对每个预期结果将流分成子流。

我还添加了一些代码,以允许重试之一。

const Observable = Rx.Observable

// Define tests here, allow requestAgain() to pass one
const testUser = (user) => user.id % 5 !== 0
const testUserAllowOneSuccess = (user) => user.id === 5 || user.id % 5 !== 0

const requestAgain = (login) => Observable.of(login)
  .switchMap(login => jQuery.getJSON("https://api.github.com/users/" + login ))
  .map(x => {
    if(!testUserAllowOneSuccess(x)) {
      console.log("logging error retry attempt", x.id);
      throw new Error('Invalid user: ' + x.id)
    }
    return x;
  })
  .retry(3)
  .catch(error => Observable.of(error))

const userStream = Observable.fromPromise(jQuery.getJSON("https://api.github.com/users"))
  .concatAll()

const passed = userStream.filter(x => testUser(x))
const failed = userStream.filter(x => !testUser(x))
  .flatMap(x => requestAgain(x.login))

const retryPassed = failed.filter(x => !(x instanceof Error))
const retryFailed = failed.filter(x => (x instanceof Error))
  .toArray()
  .map(errors => { throw errors })

const output = passed.concat(retryPassed, retryFailed)

output.subscribe(
  x=> console.log('subscribe next', x.id ? x.id : x), 
  e => console.log('subscribe error', e)
);

工作示例CodePen

然后用from转换诺言是不可重复的

示例: https//stackblitz.com/edit/rxjs-dp3md8

但是,使用Observable.create效果很好

如果有人有使诺言可重复的解决方案,请发表评论。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM