177

According to this artcle, onComplete and onError function of the subscribe are mutually exclusive.

Meaning either onError or onComplete events will fire up in my subscribe.
I have a logic block which needs to be executed whether I receive an error, or I finish my steam of information successfully.

I looked up for something like finally in python, but all I found is finally which needs to be attached to the observable I create.

But I want to to do that logic only when I subscribe, and after the stream has ended, whether successfully or with an error.

Any ideas?

martin
  • 93,354
  • 25
  • 191
  • 226
Amir Tugi
  • 2,386
  • 3
  • 16
  • 18

3 Answers3

199

The current "pipable" variant of this operator is called finalize() (since RxJS 6). The older and now deprecated "patch" operator was called finally() (until RxJS 5.5).

I think finalize() operator is actually correct. You say:

do that logic only when I subscribe, and after the stream has ended

which is not a problem I think. You can have a single source and use finalize() before subscribing to it if you want. This way you're not required to always use finalize():

let source = new Observable(observer => {
  observer.next(1);
  observer.error('error message');
  observer.next(3);
  observer.complete();
}).pipe(
  publish(),
);

source.pipe(
  finalize(() => console.log('Finally callback')),
).subscribe(
  value => console.log('#1 Next:', value),
  error => console.log('#1 Error:', error),
  () => console.log('#1 Complete')
);

source.subscribe(
  value => console.log('#2 Next:', value),
  error => console.log('#2 Error:', error),
  () => console.log('#2 Complete')
);

source.connect();

This prints to console:

#1 Next: 1
#2 Next: 1
#1 Error: error message
Finally callback
#2 Error: error message

Jan 2019: Updated for RxJS 6

martin
  • 93,354
  • 25
  • 191
  • 226
  • 1
    Interesting that it is kind of the opposite pattern of Promises, in that the `finally()` method is appended first, and the subscription imperatively coerces pass/fail. – BradGreens Mar 17 '17 at 21:15
  • 9
    Yeah, that's too bad. One would think that the `finally` block would come last in your code. – d512 Jun 08 '17 at 20:30
  • 1
    I liked the promise system of Angular JS...As d512 says I expected "finally" to be the last...Don't like this at all... – Sampgun Mar 08 '18 at 11:39
  • 11
    As of RXJS 5.5, "finally" is no longer an Observable method. Use the "finalize" operator instead: source.pipe(finalize(() => console.log('Finally callback'))).subscribe(...); https://github.com/ReactiveX/rxjs/blob/master/doc/pipeable-operators.md – Stevethemacguy Jun 06 '18 at 18:29
  • 3
    the problem with finalize is it waits for a "complete()" call. what if you want a finally on each emission (if observable emission is success do _a_, if it errors, do _b_ instead .. in both cases, do _c_)? – roberto tomás May 04 '20 at 20:11
  • @robertotomás finalize waits for either complete() or error() (https://rxjs.dev/api/operators/finalize) – santos Apr 18 '23 at 15:12
144

The only thing which worked for me is this

fetchData()
  .subscribe(
    (data) => {
       //Called when success
     },
    (error) => {
       //Called when error
    }
  ).add(() => {
       //Called when operation is complete (both success and error)
  });
Hari Das
  • 10,145
  • 7
  • 62
  • 59
  • I used this to fetch table data after a delete file subscription, and it worked like a charm, thank you! – borgmater Jan 04 '21 at 06:29
  • Do you know if this one also with without having anything in the subscribe method ?, like `.subscribe().add(() => {})` ? – Raphaël Balet May 05 '22 at 13:14
  • It doesn't work if you're unsubscribing it before it have the time to complete. I would go for the accepted answer, which do not have this problem – Raphaël Balet Nov 04 '22 at 21:36
38

I'm now using RxJS 5.5.7 in an Angular application and using finalize operator has a weird behavior for my use case since is fired before success or error callbacks.

Simple example:

// Simulate an AJAX callback...
of(null)
  .pipe(
    delay(2000),
    finalize(() => {
      // Do some work after complete...
      console.log('Finalize method executed before "Data available" (or error thrown)');
    })
  )
  .subscribe(
      response => {
        console.log('Data available.');
      },
      err => {
        console.error(err);
      }
  );

I have had to use the add medhod in the subscription to accomplish what I want. Basically a finally callback after the success or error callbacks are done. Like a try..catch..finally block or Promise.finally method.

Simple example:

// Simulate an AJAX callback...
of(null)
  .pipe(
    delay(2000)
  )
  .subscribe(
      response => {
        console.log('Data available.');
      },
      err => {
        console.error(err);
      }
  )
  .add(() => {
    // Do some work after complete...
    console.log('At this point the success or error callbacks has been completed.');
  });
pcasme
  • 493
  • 4
  • 9
  • 4
    The finalize operator must have changed since this answer was posted. Using the code you posted stating the finalize operator is being called prematurely, it works as expected for me. – wizloc Mar 22 '21 at 21:51