RxJS重试不按预期行事?

时间:2017-10-15 23:10:04

标签: javascript rxjs reactive-programming

我很难获得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无法实现?简而言之,我想异步执行值操作,将所述操作重新定义有限次数,但允许其他有效操作并行继续。我已经使用promises实现了类似的东西,但是代码相当混乱,我希望RxJS能让我更好地阅读。

4 个答案:

答案 0 :(得分:2)

我看到一些可能导致问题的问题:

1)在你的map语句中,你通过返回一个observable来进行抛出。如果成功案例也返回了一个observable,然后你进行了扁平化(使用concat,switch或merge),那么这是合适的,但是你的成功案例只会发出一个值并且永远不会变平。因此,当返回Throw observable时,它永远不会变平,因此只会被传递并记录可观察对象。

相反,你可以做一个简单的javascript throw 'error'(虽然考虑抛出一个错误对象而不是一个字符串)

2)当重试出错时,它将重新订阅。如果您正在使用observable来进行提取,这将意味着它将再次进行提取。但是,您使用promise进行了提取,然后仅从该promise中创建了一个observable。因此,当您重新订阅observable时,它将最终使用与第一次执行的完全相同的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请求。如果get请求因任何原因失败,它应该尝试总共3次以再次到达该端点。但是,这种获取请求的失败不应该阻止其他请求发生,它们应该并行发生。此外,如果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'
  });

答案 1 :(得分:1)

您的地图函数返回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)) ...

确保重试正确的事情

#include <stdio.h>
#include <stdbool.h>


int main()
{
    int row;
    int col;
    int x;
    int y;
    int i = 9;
    int count[i];


    printf("Enter the size of your array: ");
    scanf("%d %d", &row, &col);
    int arr[row][col];

    //This will read the rows
    for (int x = 0; x < row; ++x) {
        for (int y = 0; y < col; ++y) {
            printf("Enter row %d: ", x);
            scanf("%d", &arr[x][y]);
        }
    }

    //This will create a count for the rows
    for (x = 0;  x < row; ++x) {
        for (y = 0; y < col; ++y) {
            ++count[arr[x][y]];
        }
    }

    //This will count if digits repeat in the rows and print it
    printf("\nTotal count for each digit:\n");
    for (int j = 0; j < 10; ++j) {
        printf("Digit %d occurs %d time%s\n", j, count[j], count[j] > 1 ? "s" : ""); 
    }
    return 0;
}

答案 2 :(得分:1)

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

要重试个别故障,您需要分解各个请求并重试这些请求。

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

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

sender.tag

工作示例CodePen

答案 3 :(得分:0)

然后用from进行的转换承诺不可重复

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

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

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