Как подождать внутри метода подписки RxJS

Внутри объекта RxJS подписывается обратный вызов, я хочу await в функции async. Ниже приведен пример кода, который транспилер typescript жалуется на высказывание:

Ошибка: (131, 21) TS2304: Не удается найти имя "ожидание".

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
  });
}

Обычно я вижу это при попытке вызвать ожидание внутри функции async. Должен ли я каким-то образом сделать обратный вызов subscribe async, или я собираюсь сделать это неправильно? Функция saveToDatabase равна async и возвращает обещание, разрешающее первичный ключ базы данных, который был записан.

Ответ 1

Вам не нужно использовать await или конвертировать Promise в Observable.


CF это Tweet от Бен Леша:

enter image description here


Вот пример с макетом для функции saveToDatabase:
(и рабочий 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();

Вывод:
enter image description here

Ответ 2

вы можете просто добавить асинхронную подпись к анонимному вызову функции в подписке

 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

Вот мой метод решения этой проблемы

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

Вот что я делаю, меняю Обязательное на Обещание

Примечание: здесь единственная проблема в том, что это одноразовое шоу, т.е. если вы подпишитесь, как только вы не сможете получить к нему доступ снова. Подходит мне так делиться здесь.

Обновление: Обещанные легко на одно время обещания просто использовать toPromise перед подписчиком (блог). Так что для приведенного выше случая это будет так:

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

Ответ 4

Вы можете попробовать следующее:

ngOnInit() {
  this.subscriber = dateSubscription.subscribe((date: Date) => {
      (async () => {
          let dbKey = await this._someService.saveToDatabase(someObject);
          // ... some other code here
      })();
  });
}

Ответ 5

Возможно, обновленный ответ на эту проблему. У меня была аналогичная необходимость ждать ответов, и вместо того, чтобы использовать метод await или setTimeout() внутри реализации Observable, теперь более чистым способом является использование встроенного метода RxJS interval() перед завершением подписки.

Попробуйте это в сервисе:

import { interval } from 'rxjs';
...

// inside your method
const source = interval(100);
    source.subscribe(x => {
        subject.next(CONNECTIONS_DATA);
        subject.complete();
});
return subject;

Документы RxJS: https://rxjs-dev.firebaseapp.com/guide/subject#reference-counting

Надеюсь, немного полезно, если кто-то пытается сделать это в последнее время.

Ответ 6

Вы не можете await Наблюдаемые напрямую, однако вы можете await a Promise. Вы можете просто использовать метод .toPromise() для подписки на наблюдаемые. Рассмотрим следующее:

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

Когда вы await обещаете dateSubscription, вам будет передан объект Date. Затем вы можете продолжить следующую строку выполнения, что делает чтение кода более последовательным.

Некоторые люди думают, что angular не будет ждать завершения ngOnInit, у него нет выбора. Взгляните на полученный JavaScript из данного TypeScript здесь. Как вы можете видеть, ngOnInit будет вызывать awaiter, который внутренне управляет и выполняет базовую машину состояний (генератор). angular не имеет никакого контроля над этим. Он просто хочет, чтобы этот метод вызывался.

Ответ 7

async ngOnInit() { } - некорректная подпись, так как Angular определяет интерфейс OnInit. Он должен возвращать void:

 export interface OnInit { ngOnInit(): void; }

Если у вас есть какие-либо обещания для обработки после dateSubscription, вы можете использовать Observable.fromPromise как

 dateSubscription
 .flatMap(x=>
      Observable.defer(Observable.fromObservable(this._someService.saveToDatabase(someObject)))
   ).subscribe()