How to await inside RxJS subscribe method

拈花ヽ惹草 提交于 2019-12-21 07:22:28

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!