Search code examples
javascriptangularpromisees6-promisereactivex

How can I use Observables instead of Promises?


I have a service with some methods, most of them require a certain callback to be completed before it can do its stuff. With Promises, in pseudo, it is very easy to do this:

ready = http.get(stuff); // Returns a promise, resolves after a while

methodOne() { // methods sometimes called before promise resolves
    this.ready.then(_ => { 
        // doStuff
    });
}

methodTwo() {
    return this.ready.then(d => {
        // doOtherStuff
    });
}

Basically I need to do the stuff, only when i'm sure the service is ready. I actually only need to check if it's ready (what methodOne is doing, just illustrating with methodTwo, that it's easy to more stuff as well).

I want to try and go all in on Observables, but for this specific case, I find it really hard to compete with a similar solution for Observables.

Promises will remember the value and know if it got resolved. An Observable is somewhat more complex and it seems that creating this same flow is troublesome. I need whatever is subscribing to the Observable, to known when it's ready. Some times the method is called early - before the Observable emits and sometimes late, after the Observable already emitted.

I have this right now, but it doesn't seem to work:

this.ready$ = someObservable // Will fire after a litle while but never finish - i only need the first to check though.
  .publishReplay(1).refCount(); // Trying to replay if subscription comes after emit.

this.ready$.subscribe(_ => {
    // This will be called
});

methodOne() { 
    this.ready$.subscribe(_ => {
        // Not called
    });
};

Perhaps i misunderstood the use of publishReplay and refCount?


Solution

  • I think what you're looking for is AsyncSubject. It mimics the promises behavior very well. Here is the description:

    The AsyncSubject is a variant where only the last value of the Observable execution is sent to its observers, and only when the execution completes.

    Here is how it can be used in your case:

    subject = new AsyncSubject();
    ready = streamOfData(stuff).first().subscribe(subject);    
    methodOne() {
        return this.subject.asObservable();
    }
    

    The subject subscribes to the underlying observable returned by the first operator and waits until it's complete. It collects all the subscribers but doesn't send any values to them. As soon as the underlying observable completes it remembers the value and sends it to the collected subscribers. All new future subscribers will be immediately passed this stored resolved value.

    Here is the simple example that demonstrates that you can subscribe before or after the observable completes:

    const subject = new AsyncSubject();
    const o = subject.asObservable();
    o.subscribe((v) => {
      console.log(v);
    });
    interval(500).first().subscribe(subject);
    
    setTimeout(() => {
      o.subscribe((v) => {
        console.log(v);
      });
    }, 2000);