В моем приложении 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
?
Заранее спасибо.
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(...)
, потому что это ненужно и не работает с ним.
В настоящее время вы возвращаете всю подписку. Чтобы исправить это, вам нужно использовать toPromise
getUserData(uid) {
return this.fireStore.collection('users').doc(uid).valueChanges().toPromise()
}
Поскольку вы возвращаете промис в fn выше, нет необходимости создавать новый промис
someMethod() {
...
this.getUserData(uid)
.then(() => {...})
...
}
Реализация ggradnig является правильным решением, однако я хотел бы провести более глубокий анализ того, ПОЧЕМУ это работает, чтобы не было путаницы, если кто-то столкнется с этой проблемой в будущем.
Когда вы подписываетесь на наблюдаемое, большую часть времени вы передаете только одну функцию обратного вызова, которая описывает, как вы хотите обрабатывать данные из потока при их получении. На самом деле существует 3 разных обратных вызова, которые можно включить в наблюдатель для разных типов событий. Они есть:
следующий — Вызывается при получении данных из потока. Так что, если вы делаете запрос на получение статистики покемонов, это будет вызвать «следующую» функцию обратного вызова и передать эти данные в качестве Вход. В большинстве случаев это единственные данные, которые вас интересуют. создатели rxjs знали об этом, поэтому, если вы включите только 1 обратный вызов в подписку, подписка по умолчанию будет передача «следующих» данных в этот обратный вызов.
ошибка — довольно понятно. Если ошибка возникает в вашем наблюдаемом объекте и не обнаружена, он вызовет этот обратный вызов.
Если вы хотите иметь дело со всеми различными типами данных, исходящих от наблюдаемого объекта, вы можете написать наблюдатель в своей подписке, который выглядит примерно так:
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 =>{
});
}
Удалите вызов
new Promise
в вашем потребителе.