sparrow123
sparrow123

Reputation: 55

Why my custom observable behaviour is different when i transform it to Promise?

I have the following observables

  callOne() {
    return new Observable(subscriber => {
      subscriber.next(true);
      setTimeout(() => {
        subscriber.complete();
      }, 3000)
    })
  }

  callTwo() {
    return new Observable(subscriber => {
      subscriber.next(false);
      setTimeout(() => {
        subscriber.complete();
      }, 1000)
    })
  }

so when i call subscribe to them


 this.callOne().subscribe(data => {
      console.log(data);
    })

    this.callTwo().subscribe(data => {
      console.log(data);
    })

i get immediately true and false printed, even that i setted complete method in the setTimeout. With that i am saying that in x miliseconds there can't be emitted new values after the execution of the complete methhod.

When i try the same but this time the observables are converted into promises


 let response1 = await this.callOne().toPromise();
    console.log('response1', response1);
    let response2 = await this.callTwo().toPromise();
    console.log('response2', response2);

then i get printed true from the callOne observable in 3000 miliseconds. Why is that ?

Why when i have promise the complete method is taken into consideration with the setTimeout but with the observable it is not ?

Upvotes: 0

Views: 52

Answers (3)

Chris Hamilton
Chris Hamilton

Reputation: 10984

I would imagine the promise does not resolve until the subscriber.complete() call. Since you used await, execution will not continue until the promise resolves.

This article actually explains that the promise returned from toPromise() waits for the observable to complete, then returns the last value: https://levelup.gitconnected.com/rxjs-operator-topromise-waits-for-your-observable-to-complete-e7a002f5dccb

toPromise() is deprecated though, so I wouldn't bother using it. You can just wrap Observables in Promises instead. Then you can control exactly when they resolve.

  callOne() {
    const observable = new Observable((subscriber) => {
      subscriber.next(true);
      setTimeout(() => {
        subscriber.complete();
      }, 3000);
    });
    return new Promise((resolve) => {
      observable.subscribe((result) => resolve(result));
    });
  }

This promise will resolve on the first call of subscriber.next() which I believe is what you were expecting to happen.

Keep in mind Observables can call .next() repeatedly, but Promises can only resolve once.

Upvotes: 1

Alejandro Lora
Alejandro Lora

Reputation: 7832

Observables and promises are different things even though they might convert one into another.

What you are facing is expected. The promise does not "resolve" until the observable completes, so only after completion it run the logic, and that is how the promises work. Observable in that sense are different.

In fact, if you do not complete the stream, in the subscribe you still getting the code run, but in the promise as the stream never completes, it won't run anything. A very common mistake is to convert into promise from long-life hot observables.

Upvotes: 1

Kaan Yesil
Kaan Yesil

Reputation: 102

you should change something in your code. You have to run next method in setTimout. because when you run next method observables triggered and also you subscribe an observable it will invoke.

callOne() {
  return new Observable(subscriber => {

    setTimeout(() => {
      subscriber.next(true); // you can use like this
      subscriber.complete(); 
    }, 6000)
  })
}

But when you use await it is working like then() and it will invoke when everything is done like complete.

Upvotes: 2

Related Questions