Joshua Ohana Joshua Ohana - 1 month ago 13
Javascript Question

Observer subscribe not firing on first load to Component

I have a store which exposes an asObservable. A view subscribes to this and displays the data. It was working on first load since I've changed it to a Singleton service, when I first go to the page no data is displayed and no subscribe event triggered. After I perform some operation on the data which causes the subject.next to be called it updates and the view updates and everything is great.

I've ensured this is not a race condition problem and that the data is in the store at the time the page's constructor is called.

Sequence of events is as follows (confirmed during debugging):


  1. Data is loaded from server

  2. subject.next() is called with correct data

  3. component constructor is called which subscribes to observer



No subscribe event fires and no data populates, even though it exists in the store. If I then perform some kind of crud operation on the list everything cascades through as expected, next() is called, and a subscribe event occurs.

How can I get the page to read data on page load?


data.store


protected _subject$: Subject<Array<any>>;
this._subject$.next(newData); // confirmed via console this happens with good data

get contacts$(): Observable<any> {
return this._subject$.asObservable();
}



component - I've tried both putting the below block in the constructor and in ngAfterViewInit, same result


// this happens after the above data is already set
this.contacts$.subscribe(data => {
// this block does not execute on page load
});

Answer

Sequence of events is as follows (confirmed during debugging):

  1. Data is loaded from server
  2. subject.next() is called with correct data
  3. component constructor is called which subscribes to observer

It's because the vanilla Subject doesn't buffer anything. Once an item is emitted, if there is no one subscribed at that exact time, then the message is gone forever and no one will receive it.

If you want to keep the last message buffered (for all subscribers), then you can use the BehaviorSubject or the ReplaySubject

import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { RelaySubject } from 'rxjs/ReplaySubject';

sub1 = new BahaviorSubject<any>(null);
sub2 = new ReplaySubject<any>(1);

Semantically, the BehaviorSubject represents a value that changes over time, and you initialize it with the initial value. It will keep the last item always buffered until the next item pushes it out.

The Semantics of the ReplaySubject is to buffer the number of items emitted (up to the buffer size), and send them all the subscribers when subscribed to. We can initialize the ReplaySubject with a buffer size. A buffer size of 1, will make it behave just like a BehaviorSubject (but we don't need to initialize it with a value).