在 http 请求管道中使用 rxjs repeatWhen,以便在未返回所需响应时重复请求
Utilizing rxjs repeatWhen in http request pipe in order to repeat the request if desired response is not returned
我正在尝试使用我的 http 请求管道的以下简化版本来确保如果我的响应在 res.myCondition 中没有所需的数据来使用 repeatWhen 并进行另一个调用,但我很清楚不按预期的方式使用 repeatWhen。 (angular 8/ rxjs 6.5)
意图是让第一个调用进入地图,我会在其中检查 returned 数据,如果数据存在,我将 return 返回我的组件中的订阅者,但是如果 myCondition 缺少 res 我想重复 api 调用存储在 reties 中的预定次数。
关于我的 repeatWhen 实施哪里出了问题有什么想法吗?
方法一:
return this.http.get()
.pipe(map(res => res.myCondition ? res : null),
repeatWhen(res => retries-- > 0 ? of(res) : res),
catchError((error: HttpErrorResponse) => (error))
我们来分解一下repeatWhen运算符的描述:
Returns an Observable that mirrors the source Observable with the
exception of a complete. If the source Observable calls complete (1), this
method will emit to the Observable returned from notifier. If that
Observable calls complete or error (2), then this method will call
complete or error on the child subscription. Otherwise this method
will resubscribe to the source Observable (3).
(1):我可以假设 source observable this.http.get()
将在 get 请求完成后调用 complete,对吗?所以 repeatWhen
运算符最终是 notified/invoked。
(2):如果你达到了允许的重试次数那么你就不想再重试了,对吧?因此,您必须从 repeatWhen
中的匿名函数(通知程序)中 return 一个空流(一个不发出任何东西的可观察对象)
(3):否则只是 return 一个发出单个值的 Observable,表明你想重试(重新订阅)get 请求
所以您可能正在寻找的改进如下(编辑:请参阅下文为什么这个答案是错误的):
return this.http.get()
.pipe(
mergeMap(res => res.myCondition ? of(res) : EMPTY), // (*)
repeatWhen(() => retries-- > 0 ? of(true) : EMPTY),
catchError((error: HttpErrorResponse) => (error)
)
(*) 请注意,我还将您的 map
运算符替换为 mergeMap
以防止流发出 null
值。如果不满足您的条件,它 return 是一个空的可观察对象,以便不发射任何东西。
编辑:
好吧,我错了。在 rxjs playground 中尝试上面的代码后,我注意到 repeatWhen
运算符没有为源可观察对象发出的每个值调用匿名函数。相反,匿名函数只被调用一次,并且必须 return 一个基于它作为参数接收的通知可观察对象的可观察对象:
let finished = false;
return this.http.get()
.pipe(
mergeMap(res => {
finished = retries-- <= 0 || res.myCondition;
return finished ? of(res) : EMPTY;
}),
repeatWhen((notifications) =>
notifications.pipe(
takeWhile(() => !finished)
)
),
...
);
奇怪的是 notifications
对象除了 undefined
之外没有发出任何东西。对于来自可观察源 (http.get
) 的每个发射,它只为您提供 undefined
。所以我们可以强制改变 finished
标志来控制 repeatWhen
运算符。我在 SO 的另一个问题中找到了这个解决方法:
我正在尝试使用我的 http 请求管道的以下简化版本来确保如果我的响应在 res.myCondition 中没有所需的数据来使用 repeatWhen 并进行另一个调用,但我很清楚不按预期的方式使用 repeatWhen。 (angular 8/ rxjs 6.5)
意图是让第一个调用进入地图,我会在其中检查 returned 数据,如果数据存在,我将 return 返回我的组件中的订阅者,但是如果 myCondition 缺少 res 我想重复 api 调用存储在 reties 中的预定次数。
关于我的 repeatWhen 实施哪里出了问题有什么想法吗?
方法一:
return this.http.get()
.pipe(map(res => res.myCondition ? res : null),
repeatWhen(res => retries-- > 0 ? of(res) : res),
catchError((error: HttpErrorResponse) => (error))
我们来分解一下repeatWhen运算符的描述:
Returns an Observable that mirrors the source Observable with the exception of a complete. If the source Observable calls complete (1), this method will emit to the Observable returned from notifier. If that Observable calls complete or error (2), then this method will call complete or error on the child subscription. Otherwise this method will resubscribe to the source Observable (3).
(1):我可以假设 source observable this.http.get()
将在 get 请求完成后调用 complete,对吗?所以 repeatWhen
运算符最终是 notified/invoked。
(2):如果你达到了允许的重试次数那么你就不想再重试了,对吧?因此,您必须从 repeatWhen
(3):否则只是 return 一个发出单个值的 Observable,表明你想重试(重新订阅)get 请求
所以您可能正在寻找的改进如下(编辑:请参阅下文为什么这个答案是错误的):
return this.http.get()
.pipe(
mergeMap(res => res.myCondition ? of(res) : EMPTY), // (*)
repeatWhen(() => retries-- > 0 ? of(true) : EMPTY),
catchError((error: HttpErrorResponse) => (error)
)
(*) 请注意,我还将您的 map
运算符替换为 mergeMap
以防止流发出 null
值。如果不满足您的条件,它 return 是一个空的可观察对象,以便不发射任何东西。
编辑:
好吧,我错了。在 rxjs playground 中尝试上面的代码后,我注意到 repeatWhen
运算符没有为源可观察对象发出的每个值调用匿名函数。相反,匿名函数只被调用一次,并且必须 return 一个基于它作为参数接收的通知可观察对象的可观察对象:
let finished = false;
return this.http.get()
.pipe(
mergeMap(res => {
finished = retries-- <= 0 || res.myCondition;
return finished ? of(res) : EMPTY;
}),
repeatWhen((notifications) =>
notifications.pipe(
takeWhile(() => !finished)
)
),
...
);
奇怪的是 notifications
对象除了 undefined
之外没有发出任何东西。对于来自可观察源 (http.get
) 的每个发射,它只为您提供 undefined
。所以我们可以强制改变 finished
标志来控制 repeatWhen
运算符。我在 SO 的另一个问题中找到了这个解决方法: