RxJS - observable doesn't complete when an error occurs
Asked Answered
S

4

91

When I create an observable from scratch, and have the observer error, then complete, the done part of the subscription never is invoked.

var observer = Rx.Observable.create(function(observer){
    observer.onError(new Error('no!'));
    observer.onCompleted();
})

observer.subscribe(
    function(x) { console.log('succeeded with ' + x ) },
    function(x) { console.log('errored with ' + x ) },
    function() { console.log('completed') }
)

The output is:

errored with Error: no!

I'd expect it to be:

errored with Error: no!
completed

If I change the code to invoke onNext instead of onError, the observable properly completes:

var observer = Rx.Observable.create(function(observer){
    observer.onNext('Hi!');
    observer.onCompleted();
})

observer.subscribe(
    function(x) { console.log('succeeded with ' + x ) },
    function(x) { console.log('errored with ' + x ) },
    function() { console.log('completed') }
)

I get the expected output:

succeeded with Hi! 
completed

Why does it not complete when an error has occured?

Scorn answered 18/11, 2015 at 15:26 Comment(0)
N
91

That's because an error means completion, so the callback associated to onCompleted never gets called. You can review here Rxjs contract for observables (http://reactivex.io/documentation/contract.html) :

An Observable may make zero or more OnNext notifications, each representing a single emitted item, and it may then follow those emission notifications by either an OnCompleted or an OnError notification, but not both. Upon issuing an OnCompleted or OnError notification, it may not thereafter issue any further notifications.`

For error management, you can have a look at : https://github.com/Reactive-Extensions/RxJS/blob/master/doc/gettingstarted/errors.md

Nyx answered 18/11, 2015 at 15:43 Comment(0)
F
50

Another and probably the simplest solution might be using the add() function.
The statement will be always executed regardless an error occured or not (like the finally statement in most programming languages).

observer.subscribe(
    function(x) { console.log('succeeded with ' + x ) },
    function(x) { console.log('errored with ' + x ) },
    function() { console.log('completed') }
)
.add(() => {
    console.log("Will be executed on both success or error of the previous subscription")
);
Frontispiece answered 1/8, 2019 at 9:7 Comment(1)
Like a charm, when finally() does´t works with an errorKlipspringer
D
28

While I was having the same question, I bumped into this github issue.

Apparently finally method of Observable object needs to be used in this case.

Quoting from Aleksandr-Leotech from that thread:

Complete and finally are totally different things. Complete means that the observable steam was finished successfully. Because you can have many success calls. Finally means that steam has ended, either successfully or not.

It is not obvious with HTTP requests, but imagine two additional scenarios.

  1. Mouse events. You will be receiving a never-ending steam of success callbacks, but you will never receive finally or complete, because user events will never stop (unless you trigger an exception with buggy code, then you will get error and finally).

  2. Working with web sockets. You will get multiple success callbacks, but at some point in time your communication with back end will stop and you will get both complete and finally unless you have some errors, which will call error and finally.

So, you might be getting multiple or no success calls, zero or one error call, zero or one complete and zero or one finally.

Destructive answered 13/12, 2016 at 18:7 Comment(1)
"Complete and finally are totally different things" - Yeh, I saw the same github issue just now. Except the words in english are practically synonyms for one another so people assume they mean the same in other contexts (if anything, generally 'finally' is the last thing you do before something is 'complete'). A fair few rxjs questions could have been avoided by naming things right :-(Prairial
M
26

To run a callback when observable completes or errors, you should use finalize.

Ex:

this.service.yourObservable
            .pipe(
               finalize(() => {
                 // * This will always run when observable finishes the stream
                 console.log("Finally!");
                 // * callback for finally
                })  
             ).subscribe(
              {
               next: () => { // * Callback for success },
               error: () => { // * Callback for error },
               complete: () => {// * This gets called only on success }
              })
Monocoque answered 30/3, 2021 at 15:46 Comment(3)
What is the point of complete if it only fires on success?Weinert
I guess it's that you don't have to use a pipe when it successfully finishes, the code you write most likely will finish successfully then fail, so this should be the reason I guessMonocoque
@RichardBarraclough "next" can be fired several times and carries data, "complete" explicitly tells you, nothing will be fired anymore (and has no data), "error" means, something went wrong, and provides the information on what went wrong. There is no automatic recovery, an error means I'm dead and here is why, it's up to you to use a retry operator.Backbreaking

© 2022 - 2024 — McMap. All rights reserved.