最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to get catch after retryWhen? - Stack Overflow

programmeradmin1浏览0评论

I want to retry get request a few times with a second delay in case of error, but if all attemps failed, then execute error handler.

Following code retryes request, but catch is never executed. How can I fix it?

import {Response, Http} from '@angular/http';
import {Observable} from 'rxjs/Rx';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
this.http.get("/api/getData").map(data => {
  console.log('get')
  return data.json()
})
.retryWhen(err => {
  console.log('retry')
  return err.delay(1000).take(5)
})
.catch(err => {
  console.log('catch')
  this.handleHttpError(err)
  return err
})
.subscribe(data => {
  console.log('subscribe')
  console.log(data)
})

I want to retry get request a few times with a second delay in case of error, but if all attemps failed, then execute error handler.

Following code retryes request, but catch is never executed. How can I fix it?

import {Response, Http} from '@angular/http';
import {Observable} from 'rxjs/Rx';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/map';
this.http.get("/api/getData").map(data => {
  console.log('get')
  return data.json()
})
.retryWhen(err => {
  console.log('retry')
  return err.delay(1000).take(5)
})
.catch(err => {
  console.log('catch')
  this.handleHttpError(err)
  return err
})
.subscribe(data => {
  console.log('subscribe')
  console.log(data)
})
Share Improve this question asked Apr 17, 2017 at 12:20 QwertiyQwertiy 21.4k17 gold badges66 silver badges141 bronze badges 5
  • There's an example of retrying a few times in the docs, and it looks a bit different. Have you tried that? – jonrsharpe Commented Apr 17, 2017 at 12:23
  • @jonrsharpe, can't combine their examples to make it working right... – Qwertiy Commented Apr 17, 2017 at 12:30
  • Well 1. I don't think .take(5) inside the retry will help at all, and 2. have you tried using the second callback to .subscribe rather than adding a .catch? – jonrsharpe Commented Apr 17, 2017 at 12:32
  • @jonrsharpe, my example makes correct number of attempts, but never calls catch. Their example with take after retryWhen makes an infinite number of requests. – Qwertiy Commented Apr 17, 2017 at 12:36
  • @jonrsharpe, actually I have catch and subscribe in different layers of application, so that's not a good idea to add a error callback there. – Qwertiy Commented Apr 17, 2017 at 12:38
Add a comment  | 

3 Answers 3

Reset to default 13

The problem here is that when the notification Observable returned from the callback in retryWhen sends the complete notification it's propagated further as complete which is not what you want from your description.

You want to send it as error notification which means you can't use take() and use some other operator to rethrow the error.

For example like this:

Observable.defer(() => Observable.throw("It's broken"))
  .retryWhen(err => {
    console.log('retry');
    let retries = 0;
    return err
      .delay(1000)
      .map(error => {
        if (retries++ === 5) {
          throw error;
        }
        return error;
      });
  })
  .catch(err => {
    console.log('catch');
    return Observable.of(err);
  })
  .subscribe(data => {
    console.log('subscribe');
    console.log(data);
  });

You can count the number of retries in the retries variable yourself and if it reaches some limit just rethrow the error. The map() operator wraps all callbacks with try-catch blocks so any error thrown in its callable is going to be sent as error signal.

return err.delay(1000).take(5)

There should be a error after retries, for example:

return err.delay(1000).take(5).concat(Observable.throw(err))

Thanks @martin for pointing out that this code actually throws Observable instead of error.

Using modern rxjs with pipe, pipe the observable you're returning in the retryWhen operator with some logic that will throw an error if a certain condition is met.

https://stackblitz.com/edit/rxjs-retrywhen-to-catch

import { interval, of, throwError, timer } from 'rxjs'; 
import { catchError, mergeMap, switchMap, retryWhen, tap } from 'rxjs/operators';

const source = interval(1000);

const example = source.pipe(
  // simulate error
  switchMap(count => count > 3
    // this will be caught by retryWhen
    ? throwError('Error from source!')
    : of(count)
  ),
  retryWhen(err$ => err$.pipe(
    // display error from source
    tap(console.error),
    // i === index, AKA how many tries
    mergeMap((err, i) => i > 1
      // throw error, which is caught by catchError
      // or second argument to subscribe function if catchError not used
      ? throwError('Error from retry!')
      // wait specified duration before retrying
      : timer(3000)
    )
  )),
  catchError(err => of('There was an error, but we handled it. 
发布评论

评论列表(0)

  1. 暂无评论