SamuraiJack
SamuraiJack

Reputation: 5539

Why won't two subscriptions to an observable without shareReplay() execute code twice?

I was trying to figure out RxJs. ShareReplay operator in particular. As per my understanding if there are two subscriptions to an observable then the observable should be executed twice. Unless there is a shareReplay involved. Obviously my understanding is incorrect because that is not what I is happening here. Could someone please help me understand this?

export class TestComponent implements OnInit {
      constructor() {}
      i = 0;
      ngOnInit() {
        console.clear();
        let ob = this.httpcall().pipe(map(d => d));
        let ob1$ = ob.pipe(map(d => d.toUpperCase()));
        let ob2$ = ob.pipe(map(d => d.toLowerCase()));

        ob1$.subscribe(d => {
          console.log(d);
        });
        ob2$.subscribe(d => {
          console.log(d);
        });
      }

      httpcall() {
        console.log("called");
        this.i++;
        return of("server cAlled:" + this.i);
      }
    }

Output:

called
 SERVER CALLED:1
 server called:1

The counter i did not get incremented to two even though there are two subscriptions and no shareReplay involved.

I was expecting(without shareReplay):

 called
 SERVER CALLED:1
 called
 server called:2

And with let ob = this.httpcall().pipe(map(d=>d),shareReplay()); I was expecting:

 called
 SERVER CALLED:1
 server called:1

Upvotes: 2

Views: 329

Answers (3)

Goga Koreli
Goga Koreli

Reputation: 2947

That is because httpcall() is called only once via following line let ob = this.httpcall().pipe(map(d => d));. So it means that Observable that is returned by httpcall() will be reused thereafter.

If thats clear, now think about ob, which is source Observable. If you subscribe to this Observable you are going to get of("server cAlled:" + this.i); where this.i = 1. this.i is only going to increase if method httpcall() is executed once more, but it is not. Instead you are subscribing to the cold Observable ob which will just print what was used to create it. The value of this.i (which equals to 1) is already stored inside Observable ob and it is not going to be changed until new instance is created, no matter how many times you subscribe to ob.


Now lets look at the following code which is different:

    let ob1$ = this.httpcall().pipe(map(d => d)).pipe(map(d => d.toUpperCase()));
    let ob2$ = this.httpcall().pipe(map(d => d)).pipe(map(d => d.toLowerCase()));

In this situation httpcall() is called twice, thus this.i++; would happen twice and you would get what you were thinking to get.

Upvotes: 0

Robert
Robert

Reputation: 2824

it seems that of return observable of it's own, so it's body doesn't get executed twice, so i recreate the example to show the difference between normal observable and of

export class AppComponent implements OnInit {
  constructor(
  ) {
  }

  i = 0;

  ngOnInit() {
    console.clear();
    const ob = this.httpCall1();
    const ob1$ = ob.pipe(map(d => d.toUpperCase()));
    const ob2$ = ob.pipe(map(d => d.toLowerCase()));

    ob1$.subscribe(d => {
      console.log(d);
    });
    ob2$.subscribe(d => {
      console.log(d);
    });

    this.i = 0;

    const ob2 = this.httpCall2();
    const ob3$ = ob2.pipe(map(d => d.toUpperCase()));
    const ob4$ = ob2.pipe(map(d => d.toLowerCase()));

    ob3$.subscribe(d => {
      console.log(d);
    });
    ob4$.subscribe(d => {
      console.log(d);
    });
  }

  httpCall1() {
    return of('server1 called:' + ++this.i).pipe(
      tap(d => console.log('tap1', d)),
    );
  }

  httpCall2() {
    return new Observable<string>((observer) => {
      this.i++;
      observer.next('server2 called: ' + this.i);
      observer.complete();
    }).pipe(
      tap(d => console.log('tap2', d)),
    );
  }

}

Upvotes: 0

Nicholas Tower
Nicholas Tower

Reputation: 84982

When you call subscribe, that will cause the observable to do everything it was defined to do. It was defined using of("server cAlled: 1");, which is then passed into a map operator. So since you subscribed twice, of will do its thing twice, and map will do its thing twice.

You happened to create the observable inside a function named httpcall, but the observable doesn't know anything about httpcall. httpcall will not be invoked an additional time.

If you want the incrementing of this.i to be part of what happens when subscribing, then you may need to use Observable.create. For example:

httpcall() {
  return Observable.create((observer) => {
    this.i++;
    observer.next("server called: " + this.i);
    observer.complete();
  })
}

Upvotes: 2

Related Questions