Search code examples
facebook-javascript-sdkfrprxjs5

rxjs5: defer creation of an observable until another observable emits certain value


I'm trying to create an observable facebook jsdk, so I have a method, with .create()d observable that looks for example like this

getLoginStatus$() {
  return Observable.create((observer: Observer<FBResponseObject>) => {
    try {
      // this._fb is window.FB
      this._fb.getLoginStatus((resp: FBResponseObject) => {
        if (resp.error) {
          observer.error(resp.error);
        } else {
          observer.next(resp);
          observer.complete();
        }
      }, force);

    } catch (error) {
      observer.error(error);
    }

    return function () {};
  });
}

the problem is, that since facebook sdk is being loaded asynchronously, it might still not be available at the time I subscribe to this method. So I have a Subject called fbSdkReady$ I next() to true once it becomes available. Now the question is how to connect these, so when I subscribe to getLoginStatus$ it first waits for fbSdkReady$ to become ready and only then creates the observable.

I tried using delayWhen(() => fbSdkReady$.filter(r => !!r), which works fine for waiting for fbSdkReady$ to be ready, but the Observable.create is called immeditealy nevertheless and thus errors outs because FB is still not ready.

What can I do to defer creation of that observable?


Solution

  • You already have a fbSdkReady$. However if I understand you correctly it will only emit once the sdk is loaded once. This is a problem because if you subscribe to it later, you will have missed the load event and wont know if you need to wait longer or if it was already yielded. You should make the stream repeat the ready value once it is available. You can do this by calling fbSdkReady$.cache(1), but since it is backed by a subject you could also replace it with a new Rx.replaySubject(1).

    Now we got that out of the way, you can just use the fbSdkReady$ as the base for getLoginStatus$().

    fbSdkReady$.switchMap(() => getLoginStatus$())
    

    Notice I used switchMap because it clearly communicates there will only be once instance of getLoginStatus$() active at any time. But since your source only emit once, you can also use mergeMap or flatMap.

    Ps. I do hope you also call complete() on the subject and not just next(). It is good practice to signal Observables that you are done with them.