How to await inside RxJS subscribe method

Update: Found easy for one time promises just use toPromise in front of the subscriber (blog). So for the above case it will be like this:

const title = await this.translate.get('MYBOOK-PAGE.PAGE_TITLE').toPromise();

Old way: Here's my method of solving this issue

const title = await new Promise<string>(resolve => 
  this.translate.get('MYBOOK-PAGE.PAGE_TITLE')
   .subscribe(translated => {
     resolve(translated)
   }));

Here what I'm doing is changing my Observable to a Promise

Note: Here the only problem is this is one time show ie. if you subscribe once you won't able to access it again. Suited me so sharing here.


you can just directly add async signature to the anonymous function call in subscribe

 this.subscriber = dateSubscription.subscribe(async (date: Date) => {
    let dbKey = await this._someService.saveToDatabase(someObject);
    // wait for db write to finish before evaluating the next code
    // ... some other code here
  });

You do not need to use await, nor need to convert your Promise to an Observable.


CF this Tweet from Ben Lesh :

enter image description here


Here's an example with a mock for the function saveToDatabase :
(and the working Plunkr : https://plnkr.co/edit/7SDLvRS2aTw9gYWdIznS?p=preview)

const { Observable } = Rx;

const saveToDatabase = (date) =>
  new Promise(resolve =>
    setTimeout(() =>
      resolve(`${date} has been saved to the database`),
      1000));

const date$ = Observable.of(new Date()).delay(1000);

date$
  .do(x => console.log(`date received, trying to save it to database ...`))
  .switchMap(date => saveToDatabase(date))
  .do(console.log)
  .subscribe();

Output :
enter image description here


You cannot await Observables directly, however you can await a Promise.

Update

The .toPromise() method is being deprecated in RxJS 8, so you can use lastValueFrom() or firstValueFrom().

import { lastValueFrom } from 'rxjs';
this.categories = await lastValueFrom(categories$);

Thanks to Robert Rendell's comment.

Prior to RxJS v8

You can simply use the .toPromise() method on the observables subscription. Consider the following:

async ngOnInit() {
  const date = await dateSubscription.toPromise();      
  let dbKey = await this._someService.saveToDatabase(someObject);
}

When you await the promise of the dateSubscription you'll be handed a Date object. Then you can continue with the next line of your execution, which makes reading your code more sequential.

Some people are thinking the angular will not wait for the ngOnInit to complete, it does not have a choice. Take a look at the resulting JavaScript from the given TypeScript here. As you can see, the ngOnInit will invoke the awaiter which internally manages and executes the underlying state-machine (generator). Angular doesn't have any control over that. It simply wants that method to invoke.