13

I need to execute some code when Observable is completed depending on whether has finalized with error or without. I have this code:

const obs = getMyObservable().pipe(finalize(() => {
    //here
}));

As you see, I'm using finalize operator, but I can't know if has finalized with or without error. Is there some kind of doOnComplete or doOnError operators in rxjs?

Héctor
  • 24,444
  • 35
  • 132
  • 243

4 Answers4

13

According to https://www.learnrxjs.io/

With the latest RXJS you can use this 3 operators

const obs = getMyObservable().pipe(                                       // Let's assume 'obs' returns an array
    tap(() => console.log('Action performed before any other')),
    catchError(() => { console.error('Error emitted'); return of([]); }), // We return [] instead
    finalize(() => console.log('Action to be executed always'))           // Either Error or Success
);
obs.subscribe(data => console.log(data));  // After everything, we log the output.

Hope it helps

EDIT based on JoniJnm comment

To be More specific, there are, 3 main Pipes:

  1. Pipes that does alter the result before subscription.
  2. Pipes that does not alter the result before subscription.
  3. Special Pipes.

Tap for example is from the second type, it can takes the input from the observable or previous pipes and do anything with it but cannot change the result of the pipe for the next step.

Map is similar but it belongs to the first type of Pipe, it takes an input and has to return an output that can be used in the next pipe or final subscription.

Finalize is a special pipe which does the same as Tap but after subscription. It is good for example to log final results or to cancel subscription after it completes.

CatchError is a pipe which alters the result but it is only called if the previous step has thrown an error. This is used to avoid unhandled error and you should return an observable "default" instead of the failed observable (so we handle the error and the app does not break).

You can guess when your observable had an Error if catchError has been launched and handle it straight away before it reach the subscription.

If this pipe is not launched, the result is considered without error.

funkid
  • 577
  • 1
  • 10
  • 30
Ignacio Bustos
  • 1,415
  • 2
  • 17
  • 26
  • 1
    This doesn't solve the problem. A function (in a pipe) that is executed after everything but knowing if there was an error or not. – JoniJnm Oct 29 '18 at 17:28
  • 1
    The order of execution is `Observable -> Pipes in order (except finalize) -> subscribe -> finalize Pipe` with this. A subscription gets what **catchError()** has done if there was an error. I will explain more in the answer. – Ignacio Bustos Nov 11 '18 at 16:02
  • If only the true documentation for rxjs could be as straightforward as this answer. Great example and explanation. – Jessy Feb 26 '19 at 15:36
1

The accepted answer is wrong. Why? Because 'finalize' will run no matter what. See case Why is finalize lifted in RxJS?. In short, finalize will run whether or not there is an error or not. Use a variable (likely in a closure) to figure out the difference:

let failed = false
source$.pipe(
   map(doSomethingFn),
   map(doAnotherThingFn),
   catchError(e => {failed = true; return throwError(e)}),
   finalize(() => {if(!failed)IwontRunIfErrorFn})
)
tommyc38
  • 703
  • 1
  • 8
  • 21
  • Hi @tommyc38, you are right about finalize, but this is already mentioned in my response, if you check the code, finalize has already a console log and a commented line on the right explaining when this is executed, particularly mentioning wether or not there was an error. But you are right, I should have left that more clear. – Ignacio Bustos Apr 12 '21 at 17:13
0

You can use concat

concat(
 yourObservable(),
 defer(() => {
  // do something on success
  return of('success');
 })
)

according to documentation, second observable will be subscribed only if first successfully completed

0

Use the tap/do operator, ie:

tap({
  next: val => {
    console.log('on next', val);
  },
  error: error => {
    console.log('on error', error.message);
  },
  complete: () => console.log('on complete')
})
fionbio
  • 3,368
  • 2
  • 23
  • 38