问题
Inside of an RxJS subject's subscribe callback, I want to await
on an async
function. Below is a code example which the typescript transpiler complains about saying:
Error:(131, 21) TS2304:Cannot find name 'await'.
async ngOnInit() {
this.subscriber = dateSubscription.subscribe((date: Date) => {
let dbKey = await this._someService.saveToDatabase(someObject);
// wait for db write to finish before evaluating the next code
// ... some other code here
});
}
Usually I see this when trying to call await inside a non async
function. Do I somehow need to make the subscribe callback async
or am I going about this wrong? The function saveToDatabase
is async
and returns a promise resolving to the database primary key that was written to.
回答1:
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 :
回答2:
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
});
回答3:
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.
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();
回答4:
You cannot await
Observables directly, however you can await
a Promise
. 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.
回答5:
Perhaps an updated response to this problem. I had a similar need to await responses, and rather than using await
or setTimeout() method inside the Observable implementation, a cleaner way now is to use the RxJS built-in interval()
method before completing the subscription.
Try this in the service:
import { interval } from 'rxjs';
...
// inside your method
const source = interval(100);
source.subscribe(x => {
subject.next(CONNECTIONS_DATA);
subject.complete();
});
return subject;
RxJS Docs: https://rxjs-dev.firebaseapp.com/guide/subject#reference-counting
Hopefully a bit helpful if anyone is trying to do this more recently.
回答6:
async ngOnInit() { }
is incorrect signature, because this is how Angular defines OnInit
interface. It should returns void
:
export interface OnInit { ngOnInit(): void; }
If you have any promise to process after dateSubscription
you can use Observable.fromPromise
like
dateSubscription
.flatMap(x=>
Observable.defer(Observable.fromObservable(this._someService.saveToDatabase(someObject)))
).subscribe()
来源:https://stackoverflow.com/questions/43881504/how-to-await-inside-rxjs-subscribe-method