Подписка на обещание

В моем приложении Angular 7 у меня есть следующая функция:

  getUserData(uid) {
    return this.fireStore.collection('users').doc(uid).valueChanges().subscribe(data => {
      this.writeCookie(data)
      this.currentUser = data;
    })
  }

И я хочу использовать эту функцию внутри другого метода:

   someMethod() {
      ...
      new Promise(this.getUserData(uid))
         .then(() => {...})
      ...
   }

Но я не могу этого сделать, потому что TypeScript выдает ошибку:

Argument of type 'Subscription' is not assignable to parameter of type '(resolve: (value?: {} | PromiseLike<{}>) => void, reject: (reason?: any) => void) => void'. Type 'Subscription' provides no match for the signature '(resolve: (value?: {} | PromiseLike<{}>) => void, reject: (reason?: any) => void): void'.ts(2345)

Как я могу преобразовать метод getUserData() в обещание или вместо этого использовать forJoin?

Заранее спасибо.

Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Angular и React для вашего проекта веб-разработки?
Angular и React для вашего проекта веб-разработки?
Когда дело доходит до веб-разработки, выбор правильного front-end фреймворка имеет решающее значение. Angular и React - два самых популярных...
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Эпизод 23/17: Twitter Space о будущем Angular, Tiny Conf
Мы провели Twitter Space, обсудив несколько проблем, связанных с последними дополнениями в Angular. Также прошла Angular Tiny Conf с 25 докладами.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
Мое недавнее углубление в Angular
Мое недавнее углубление в Angular
Недавно я провел некоторое время, изучая фреймворк Angular, и я хотел поделиться своим опытом со всеми вами. Как человек, который любит глубоко...
Освоение Observables и Subjects в Rxjs:
Освоение Observables и Subjects в Rxjs:
Давайте начнем с основ и постепенно перейдем к более продвинутым концепциям в RxJS в Angular
9
0
17 088
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Ответ принят как подходящий

subscribe меняет тип с Observable на Subscription, вызывая ошибку типа.

Что вы, вероятно, хотите, так это преобразовать ваш Observable в Promise, сохранив при этом вызов функции. Вы можете сделать это, передав Observable через tap, а затем преобразовав результат с помощью toPromise. Так:

getUserData(uid) {
  return this.fireStore.collection('users').doc(uid).valueChanges().pipe(
    tap(data => {
      this.writeCookie(data)
      this.currentUser = data;
    }),
    first()
  ).toPromise()
}

Обязательно создайте завершающий канал, как вы можете сделать с оператором first, иначе промис никогда не разрешится.

Вы можете не указывать new Promise(...) в своем потребителе.

Удалите вызов new Promise в вашем потребителе.

ggradnig 09.04.2019 21:03

Спасибо, это работа. Но я должен удалить new Promise(...), потому что это ненужно и не работает с ним.

Vladimir Humeniuk 09.04.2019 21:04

В настоящее время вы возвращаете всю подписку. Чтобы исправить это, вам нужно использовать toPromise

getUserData(uid) {
        return this.fireStore.collection('users').doc(uid).valueChanges().toPromise()
      }

Поскольку вы возвращаете промис в fn выше, нет необходимости создавать новый промис

   someMethod() {
      ...
      this.getUserData(uid)
         .then(() => {...})
      ...
   }

Реализация ggradnig является правильным решением, однако я хотел бы провести более глубокий анализ того, ПОЧЕМУ это работает, чтобы не было путаницы, если кто-то столкнется с этой проблемой в будущем.

Когда вы подписываетесь на наблюдаемое, большую часть времени вы передаете только одну функцию обратного вызова, которая описывает, как вы хотите обрабатывать данные из потока при их получении. На самом деле существует 3 разных обратных вызова, которые можно включить в наблюдатель для разных типов событий. Они есть:

  1. следующий — Вызывается при получении данных из потока. Так что, если вы делаете запрос на получение статистики покемонов, это будет вызвать «следующую» функцию обратного вызова и передать эти данные в качестве Вход. В большинстве случаев это единственные данные, которые вас интересуют. создатели rxjs знали об этом, поэтому, если вы включите только 1 обратный вызов в подписку, подписка по умолчанию будет передача «следующих» данных в этот обратный вызов.

  2. ошибка — довольно понятно. Если ошибка возникает в вашем наблюдаемом объекте и не обнаружена, он вызовет этот обратный вызов.

  3. полный — вызывается, когда наблюдаемое завершается.

Если вы хотите иметь дело со всеми различными типами данных, исходящих от наблюдаемого объекта, вы можете написать наблюдатель в своей подписке, который выглядит примерно так:

this.http.get(“https://pokemon.com/stats/bulbasaur”).subscribe({
    next: () => { /* deal with pokemon data here */},
    error: () => {/* called when there are errors */},
    complete: () => {/* called when observable is done */}
})

Опять же, в большинстве случаев в этом нет необходимости, но важно понимать эти типы событий, когда мы вызываем метод «.toPromise()» для Observable. Когда мы конвертируем Observable в Promise, происходит то, что Promise будет разрешаться с последними «следующими» данными, выдаваемыми из Observable, как только будет вызван метод «Complete» для Observable. Это означает, что если обратный вызов «Complete» не будет вызван, Promise будет зависать на неопределенный срок.

Да, я знаю, о чем вы думаете: я все время преобразовываю свои http-запросы из Observables в Promises и никогда не сталкиваюсь с ситуацией, когда мой Promise зависает на неопределенный срок. Это связано с тем, что http-библиотека angular вызывает обратный вызов «Complete» для Observable, как только все данные получены из http-вызова. Это имеет смысл, потому что как только вы получите все данные из запроса, вам конец. Вы не ожидаете больше данных в будущем.

Это отличается от ситуации, описанной в вопросе, когда вы звоните в firestore, который, как я знаю из опыта, использует сокеты для передачи информации, а не HTTP-запросы. Это означает, что через соединение вы можете получить начальный набор данных… а затем еще данные… и затем еще данные. По сути, это поток, у которого нет определенного конца, поэтому у него никогда нет причин вызывать обратный вызов «Complete». То же самое произойдет с темами Behavior и Replay.

Чтобы обойти эту проблему, вам нужно заставить Observable вызывать обратный вызов «Complete» либо путем передачи в «first()», либо в «take(1)», который сделает то же самое, вызовите функцию обратного вызова «next» с начальным набор данных в качестве входных данных, а затем вызовите обратный вызов «Complete».

Надеюсь, это кому-нибудь пригодится, потому что эта проблема меня чертовски смущала долгое время.

Также это видео может стать отличным подспорьем, если вы все еще запутались: https://thewikihow.com/video_Tux1nhBPl_w

Если вы должны иметь .подписка в методе получитьUserData, то это по-другому.

getUserData(uid): Promise<any> {
    return new Promise((resolve, reject) => {
        this.fireStore.collection('users').doc(uid).valueChanges().subscribe({
            next: data => {
                this.writeCookie(data)
                this.currentUser = data;
                resolve();
            },
            error: err => {
                reject(err);
            }
        });
    });
}

то вы можете использовать вот так

someMethod() {
    this.getUserData(uid)
        .then(() => {...
        })
        .catch(e =>{

        });
}

Другие вопросы по теме