mbreton
mbreton

Reputation: 684

Weird case where map function is not called with a BehaviorSubject

I'm front of a weid case in my Angular App about a map's RxJS not called.

Please, take a look to this sample of code :

const userSource = new Rx.BehaviorSubject(undefined);
const user = userSource.asObservable();

user.map(user => {
  const shouldPass = !!user;
  console.log('shouldPass', shouldPass, user);
  if (user === null) {
    console.log('go to login');
    // this.router.navigate(['login']);
  }
  return shouldPass;
});
Rx.Observable
  .throwError(new Error('HTTP 401'))
  .subscribe(
    user => userSource.next(user),
    e => userSource.next(null)
  );

This code (that I translate into plain JavaScript) is placed in a function dedicated to load User entity from server (here replaced by a throw Error to simulate an HTTP error) and then emit on a BehaviorSubject. In this exemple, the user.map is called only one time during the initialization of the BehaviorSubject.

For me, it should be called twice time, right ? One time for the initialization and another time when an error is thrown from the observable.

Upvotes: 0

Views: 460

Answers (2)

Sasxa
Sasxa

Reputation: 41274

Observables are lazy (most of the time), they don't execute the code you declared until you subscribe. Subscription takes three arguments (next, error, complete) - functions that may or may not execute, depending on what is passed through observable chain.

In your example only error callback is executed, this:

Rx.Observable
  .empty()
  .subscribe(...)

would only execute complete callback (you didn't specified it).

Rx.Observable
  .of(123)
  .subscribe(...)

would execute both next and complete callbacks.

Basic rules are:

  • next callback is called if there is no error
  • error is called then there is no error
  • complete is called when observable completes and there is no error

For more info see:

Upvotes: 1

martin
martin

Reputation: 96899

The question is where do you subscribe? To user? Because that's the same as subscribing to userSource.asObservable().

I guess you want to subscribe to the result of user.map() so you need to assign it to a variable:

const userSource = new Rx.BehaviorSubject(undefined);
const user = userSource.asObservable();

const bla = user.map(user => {
  const shouldPass = !!user;
  // console.log('shouldPass', shouldPass, user);
  if (user === null) {
    // console.log('go to login');
    // this.router.navigate(['login']);
  }
  return shouldPass;
});

bla.subscribe(console.log);

Rx.Observable
  .throw(new Error('HTTP 401'))
  .subscribe(
    user => userSource.next(user),
    e => userSource.next(null)
  );

Also note that you need to subscribe to bla before Rx.Observable.throw() because otherwise you do emit into userSource.next(user) but nobody is listening (there's no observer).

With the above code you'll see false printed twice.

Upvotes: 0

Related Questions