Observable retry / retryWhen with flatmap

2020-07-15 15:35发布

问题:

I have the following code to get id and then data related to the id.

process(): Observable<any> {
    let id: number;
    return this.getId().flatMap(
        (response: number) => {
                id = response;
                return this.getData(id)
        }
    ).flatMap(
        (data: any) => {
            return Observable.of(data);
        }
    );
}

getData(id: number): Observable<any> {
    // retry if response.status is not complete
    return this.service.getData(id).map(response => return response.data); 
}

I would like to add retry logic on getData method until response.status is not complete. I tried adding retry/retryWhen after this.getData(id) but no luck. Any suggestions please?

回答1:

retry and retryWhen are suitable for error handling, meaning that the source observable has to produce an error for these operators to make an effect. I guess that in your case you want to retry not on error, but on a status that you don't like.

Probably the simplest way to achieve that would be to make service calls on some interval and take the first response with the status you want:

getData(id) {
    return Observable.interval(100)
        // make 5 attempts to get the data
        .take(5)
        .concatMap(() => this.service.getData(id))
        .filter(response => response.status === "complete")
        .take(1)
        .map(response => response.data)
}

Now getData() returns an observable that either emits a single value that corresponds to a response with a "complete" status and then completes, or completes without emitting any value after 5 unsuccessful attempts to get the data with the desired status (the answer updated in response to a comment).

Update:

It is, of course, possible to turn responses with "bad" statuses into errors and use the retry functionality, as you proposed:

getData(id) {
    return this.service.getData(id)
        .concatMap(response => response.status === "complete" ?
            Observable.of(response) :
            Observable.throw("Bad status"))
        .map(response => response.data)
        .retry(5);
}

If things go wrong, this code will try to fetch the data at most 6 times (1 initial + 5 retries) and then will produce an error that you would have to handle, for example with catch operator. One drawback is that this way does not distinguish "real" errors (e.g. network failures) from errors produced out of bad statuses, but this could be handled with retryWhen.



回答2:

I have tryed retry method and it works.

(Observable ...).retry(2).map(...).subscribe(...)

When I do a http call and the response is for example a 404 it retries the call twice.