简体   繁体   中英

RxJS : how to throw an error then catch it?

This is not related to a bug I am encountering, but rather a syntax issue.

The workflow is simple :

  • make an HTTP request that returns a boolean
  • if the boolean is true, then continue
  • if the boolean is false, log a warning, and stop the stream.

To manage that, my current code is this :

Boilerplate

private _getBoolean() { return this.http.get(...); }
private _getData() { return this.http.get(...); }

Current code

public getData() {
  return this._getBoolean().pipe(
    filter(bool => {
      if(!bool) {
        console.warn('Wrong server answer, stream stopped');
        return false;
      }
      return true;
    }),
    switchMap(bool => this._getData())
  );
}

And I don't know why, but it doesn't feel natural and optimized to me.

I thought that there would be something that simplifies the syntax, something like this

public getData() {
  return this._getBoolean().pipe(
    throwError(bool => bool ? new Error('Wrong server answer, stream stopped') : null),
    catchError(err => console.warn(err)),
    switchMap(bool => this._getData())
  );
}

Is there something along the lines of that, or do I have the correct syntax ?

Consider the following observable below that emits value 1 to 4. Let's say an error is thrown when the value is 3. That error can be caught in the catchError operator or it can be caught within the subscribe . I believe it depends on the specific use case whether you let the error bubble all the way up to the subscriber or whether it should be handled somewhere upstream of the subscriber.

of(1, 2, 3, 4).pipe(
  // Throw error when value is 3
  tap(value => { if(value === 3) throw new Error('Oops!') }),
  catchError(err => {
    console.log('Catch error in operator', err);

    // You can rethrow the same error or a new error
    // return throwError(err);

    // Or you can handle the error and return a new observable
    return of(3)
  })
).subscribe(
  value => console.log(value),
  // If `catchError` returns a new observable, then the error 
  // function here will not be called
  err => console.log('Catch error within subscribe', err),
  () => console.log('Done!')
)

Note that in this example, even if the error is being handled the observable completes and the value 4 is never emitted. If you wish to keep the observable alive when en error is encountered then have a look at this StackOverflow answer .

instead of:

public getData() {
  return this._getBoolean().pipe(
    throwError(bool => bool ? new Error('Wrong server answer, stream stopped') : null),
    catchError(err => console.warn(err)),
    switchMap(bool => this._getData())
  );
}

why not something like:

public getData() {
  return this._getBoolean().pipe(
    tap(result => !result && throwError('Wrong server answer, stream stopped')),
    switchMap(bool => this._getData()),
    catchError(err => console.warn(err))
  );
}

I'm not sure if I get your issue correctly, but you might replace

    console.warn('Wrong server answer, stream stopped');
    return false;

With

   Observable.throw('Some error cause')

And then catch it with nearest catch block in your stream, which gives you basically change to: - Stop stream if you re-throw error - Restart it if you return input observable - return completely new observable

public getData() {
  return this._getBoolean().pipe(
    filter(bool => {
      if(!bool) {
        console.warn('Wrong server answer, stream stopped');
        //return false;
        Observable.throw('I got false where I expected true')
      }
      return true;
    }),
    switchMap(bool => this._getData())
  );
}

And then:

getData()
.any()
.operator()
.you()
.wish()
.catch(e => {
  /* Here stream will be terminated on thrown error */
})

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM