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 :
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 :
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.