多个异步调用的RxJS设计模式

时间:2018-07-22 04:22:10

标签: rxjs

我是RxJS的新手,无法在以下用例中找到明确的答案:

在移动应用程序(Angular / Ionic)中,我需要(1)同时进行HTTP调用,并且仅在所有操作完成后才返回数据(例如$ q.all)。我想(2)如果调用正常工作,但其中一个满足特定条件的响应中存在嵌套值(即,用户未正确认证),则会引发错误。因为它是一个移动应用程序,所以我想(3)在调用无法正常运行(由于任何原因)的情况下进行几次重试。如果经过一定数量的重试尝试后呼叫仍然失败,我想(4)抛出错误。

根据我的研究,似乎forkJoin与q.all的工作原理相同。我有一个提供者,它返回以下内容(observableArray拥有http调用)。

return Observable.forkJoin(observableArray)

然后我可以输入一些运算符,这就是我开始努力的地方。为了检查嵌套值(2),我使用下划线方法遍历我的响应数组中的每个响应。这似乎一点都不干净。

为重试调用(3),我使用retryWhen和delayWhen。但是我不确定如何将其限制为3或4次尝试。

如果达到了限制,我将如何向订户(4)抛出错误?

.pipe(
    map(
        res => {
            _.each(res, (obs) => {
                if (!obs['success']) throw new Error('success was false')
            })
        }
    ),
    retryWhen(attempts =>
        attempts.pipe(
            tap(err => console.log('error:', err),
                delayWhen(() => timer(1000))
            )
        )
    ))

2 个答案:

答案 0 :(得分:3)

这里有一些技巧可以使您的代码干净。

1。使用Observable.iif()

  

iif接受条件函数和两个Observable。订阅操作员返回的Observable时,将调用条件函数。根据当时返回的布尔值,消费者将订阅第一个Observable(如果条件为true)或第二个(如果条件为false)。

2。使用JavaScript数组本机的every()

  

every()方法测试数组中的所有元素是否通过提供的函数实现的测试。

3。使用take()终止您的retryWhen

  

仅发射源Observable发出的第一个计数值。

因此您的代码归结为:

.pipe(
    switchMap((res) => iif(
        () => res.every(({success}) => success),
        res,//if every element in res is successful, return it
        throwError('success was false') //else if any of it is false, throw error.
        )
    ),
    retryWhen(err => err.delay(1000).take(4))
)

编辑:

如果您想在订阅时捕获错误,则需要重新抛出该错误。 .take()实际上将终止序列,也就是完成序列:

.pipe(
    switchMap((res) => iif(
        () => res.every(({success}) => success),
        res,//if every element in res is successful, return it
        throwError('success was false') //else if any of it is false, throw error.
        )
    ),
    retryWhen(err => {
        return errors.scan((errorCount, err) =>
            (errorCount >= 4 ? //is retried more than 4 times?
                    throwError(err) : //if yes, throw the error
                    errorCount++ //else just increment the count
            ), 0);
    })
)

答案 1 :(得分:0)

考虑的一种可能性是通过管道传递所需的各种运算符,例如retrymap进入observableArray所包含的forkJoin中的每个Observable中。

代码可能看起来与此类似

const observableArray = new Array<Observable<any>>();
const maxRetries = 4;

function pipeHttpObservable(httpObs: Observable<any>): Observable<any> {
   return httpObs
      .pipe(
         map(data => data.success ? data : throwError('success was false')),
         retryWhen(err => err.delay(1000).take(maxRetries))
      )
}

observableArray.push(pipeHttpObservable(httpObs1));
observableArray.push(pipeHttpObservable(httpObs2));
.....
observableArray.push(pipeHttpObservable(httpObsN));

forkJoin(observableArray).subscribe(result => do stuff with the results)
相关问题