Reputation: 197
I'm trying to use below simplified version of my http request pipeline to ensure that if my response does not have the required data in res.myCondition to utlize repeatWhen and make another call, but I'm clearly not utilizing repeatWhen the way it's meant to be. (angular 8/ rxjs 6.5)
Intention is for the first call to come into the map, where I check the returned data, if the data is there I'll return it to the subscriber back in my component, but if myCondition is missing from res I'd like to repeat the api call a pre-determined number of times stored in reties.
any thoughts on where I'm going wrong with my repeatWhen implementation?
method1:
return this.http.get()
.pipe(map(res => res.myCondition ? res : null),
repeatWhen(res => retries-- > 0 ? of(res) : res),
catchError((error: HttpErrorResponse) => (error))
Upvotes: 4
Views: 4432
Reputation: 779
Let's break down the description of the repeatWhen operator:
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): I can assume that the source observable this.http.get()
will call complete after the get request finishes, right? So the repeatWhen
operator is being notified/invoked eventually.
(2): if you reached your number of allowed retries then you don't want to retry anymore, right? So you will have to return an empty stream (an observable that doesn't emit anything) from the anonymous function (notifier) inside repeatWhen
(3): otherwise just return an observable that emits one single value to indicate that you want to retry (resubscribe) the get request
So the improvement you are probably looking for is the following (edit: see below why this answer is wrong):
return this.http.get()
.pipe(
mergeMap(res => res.myCondition ? of(res) : EMPTY), // (*)
repeatWhen(() => retries-- > 0 ? of(true) : EMPTY),
catchError((error: HttpErrorResponse) => (error)
)
(*) Please notice that I also replaced your map
operator with mergeMap
to prevent the stream from emitting null
values. If your condition is not met it returns an empty observable in order to emit nothing instead.
edit:
Okay so I was wrong. After trying the code above in a rxjs playground I noticed that the repeatWhen
operator is not calling the anonymous function for each value emitted by the source observable. Instead the anonymous function is called only once an has to return an observable that is based on the notifications observable that it receives as an parameter:
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)
)
),
...
);
The weird part is that that notifications
subject doesn't emit anything other than undefined
. For each emission from the source observable (http.get
) it gives you just undefined
. So we could imperatively mutate a finished
flag to control the repeatWhen
operator. I found this workaround in a different question on SO: RxJS repeat depending on emitted value
Upvotes: 4