1

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 ?

3 Answers3

1

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.

Sam Herrmann
  • 6,293
  • 4
  • 31
  • 50
  • I have tried this solution too, as it's the first one I have found online. The issue with this code is that error thrown is a Javascript error, and gets displayed as an actual JS error in the console. I might sound picky, but my code doesn't have a JS error, just a boolean set to false returned from the server ... So this solution doesn't suit me (but I'll upvote it because it might help others) –  Nov 09 '18 at 14:26
  • Is there anything that prevents you from using `tap(bool => { if(bool) throw new Error('Wrong server answer') })`, i.e. you create a JS error when the server gives you false? And as long as you catch it then it shouldn't show in the console. – Sam Herrmann Nov 09 '18 at 14:29
  • That's the trick, you don't actually catch it, try it, you'll see that it pops in your console. And it bothers me (well not me, my N+1) because the server response is a 200, not a 400 or 500, so it's not an error (I simplified the example, I of course don't request a simple boolean from the server) –  Nov 09 '18 at 14:38
1

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))
  );
}
dee zg
  • 13,793
  • 10
  • 42
  • 82
  • Will that catch the error if you don't return anything ? (I'm on phone I can't test right now) –  Nov 09 '18 at 16:44
  • what do you mean by not return anything? from where? and, what error in that case? – dee zg Nov 09 '18 at 16:49
0

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 */
})
Tomas
  • 3,269
  • 3
  • 29
  • 48
  • Isn't the observable prototype usage considered a bad practice in rxjs 6 ? –  Nov 09 '18 at 12:23
  • In RxJS 6 yes, it does. Unfortunately I'm basing my so far experience on 5.5 :( You should get same result by wrapping all operators except `catch` with pipe – Tomas Nov 09 '18 at 12:25
  • Well same thing, I think catch is a bad practice, you should use the second callback of the subscribe ... Hence my question, like you I have based my experience on < 6 and I haven't figured out how to manage that yet ... –  Nov 09 '18 at 12:26
  • @trichetriche could you please share some reference saying catch in observable chain is a bad practice? Second callback in subscribe has different purpose: it will handle errors in subscriptions but that observable is gone at that point of time. so its a different usage. – dee zg Nov 09 '18 at 14:08
  • btw, `Observable.throw` became `throwError()` operator – dee zg Nov 09 '18 at 14:13
  • @deezg see **Why ?** in [pipeable operators](https://github.com/ReactiveX/rxjs/blob/master/doc/pipeable-operators.md) : what touches the prototype is discouraged (at least that's how I got it) –  Nov 09 '18 at 14:23
  • @trichetriche i understand that but not sure how `catchError` & `throwError` touch prototype? – dee zg Nov 09 '18 at 14:38
  • I talked about `catch`, not `catchError` :) –  Nov 09 '18 at 14:39
  • @trichetriche oh, ok, i got that wrong then. i thought you were saying no catching (whatever the syntax is) in chain but only in second callback of `subscribe` :) – dee zg Nov 09 '18 at 16:28