订阅承诺
Subscription to promise
在我的 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()
方法转换为 Promise,或者改用 forJoin
?
提前致谢。
subscribe
将类型从 Observable
更改为 Subscription
,从而导致类型错误。
您可能想要将 Observable 转换为 Promise,同时保留函数调用。你可以通过 tap
管道 Observable 然后用 toPromise
转换结果来做到这一点。像这样:
getUserData(uid) {
return this.fireStore.collection('users').doc(uid).valueChanges().pipe(
tap(data => {
this.writeCookie(data)
this.currentUser = data;
}),
first()
).toPromise()
}
确保创建一个完成管道,就像您可以使用 first
运算符一样,否则 Promise 将永远无法解析。
您可以在消费者中省略 new Promise(...)
。
目前您正在退还整个订阅。要解决这个问题,您需要使用 toPromise
getUserData(uid) {
return this.fireStore.collection('users').doc(uid).valueChanges().toPromise()
}
由于您在上面的 fn 中返回一个 promise,因此无需创建新的 Promise
someMethod() {
...
this.getUserData(uid)
.then(() => {...})
...
}
ggradnig 的实施是正确的解决方案,但是我想更深入地分析它为什么起作用,这样以后如果有人 运行 遇到这个问题就不会造成混淆。
当您订阅一个 observable 时,大多数时候您只是传递一个回调函数,它描述了您希望如何在接收到流中的数据时对其进行处理。实际上,对于不同类型的事件,观察者中可以包含 3 种不同的回调。他们是:
next - 当从流中接收到数据时调用。因此,如果您请求获取一些口袋妖怪统计数据,它将
调用“下一个”回调函数并将该数据作为
输入。大多数时候,这是您唯一关心的数据,并且
rxjs 的创造者知道这一点,所以如果你只包含 1 个回调
function into a subscription,订阅将默认为
将“下一个”数据传入此回调。
error - 不言自明。如果在您的 observable 中抛出错误但未被捕获,它将调用此回调。
- complete - 当 observable 完成时调用。
如果你想处理从一个可观察对象发出的所有不同类型的数据,你可以在你的订阅中编写一个观察者,看起来像这样:
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 */}
})
同样,这在大多数情况下是不必要的,但是当我们在 Observable 上调用“.toPromise()”方法时,理解这些类型的事件是必不可少的。当我们将 Observable 转换为 Promise 时,一旦 Observable 上的“Complete”方法被调用,Promise 将使用从 Observable 发出的最后一个“下一个”数据来解析。这意味着如果不调用“完成”回调,Promise 将无限期挂起。
是的,我知道你在想什么:我一直将我的 http 请求从 Observables 转换为 Promise,而且我从来没有 运行 遇到我的 Promise 无限期挂起的情况。这是因为一旦从 http 调用接收到所有数据,angular http 库就会调用 Observable 上的“完成”回调。这是有道理的,因为一旦您从请求中收到所有数据,您就完成了。您不会期望将来有更多数据。
这与您调用 firestore 的问题中描述的情况不同,我从经验中知道它使用套接字来传输信息,而不是 http 请求。这意味着通过连接,您可能会收到一组初始数据……然后是更多数据……然后是更多数据。它本质上是一个没有明确结束的流,所以它永远没有理由调用“完成”回调。行为和重播主题也会发生同样的事情。
为了避免这个问题,你需要强制 Observable 调用“Complete”回调,方法是通过管道传入“first()”或“take(1)”,它们会做同样的事情,调用“next”回调函数以初始数据集作为输入,然后调用“完成”回调。
希望这对外面的人有用,因为这个问题让我困惑了很长时间。
如果您仍然感到困惑,这个视频也是一个很好的参考:https://www.youtube.com/watch?v=Tux1nhBPl_w
如果您必须在 getUserData 方法中使用 .subscription,那么这是另一种方式。
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 =>{
});
}
在我的 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()
方法转换为 Promise,或者改用 forJoin
?
提前致谢。
subscribe
将类型从 Observable
更改为 Subscription
,从而导致类型错误。
您可能想要将 Observable 转换为 Promise,同时保留函数调用。你可以通过 tap
管道 Observable 然后用 toPromise
转换结果来做到这一点。像这样:
getUserData(uid) {
return this.fireStore.collection('users').doc(uid).valueChanges().pipe(
tap(data => {
this.writeCookie(data)
this.currentUser = data;
}),
first()
).toPromise()
}
确保创建一个完成管道,就像您可以使用 first
运算符一样,否则 Promise 将永远无法解析。
您可以在消费者中省略 new Promise(...)
。
目前您正在退还整个订阅。要解决这个问题,您需要使用 toPromise
getUserData(uid) {
return this.fireStore.collection('users').doc(uid).valueChanges().toPromise()
}
由于您在上面的 fn 中返回一个 promise,因此无需创建新的 Promise
someMethod() {
...
this.getUserData(uid)
.then(() => {...})
...
}
ggradnig 的实施是正确的解决方案,但是我想更深入地分析它为什么起作用,这样以后如果有人 运行 遇到这个问题就不会造成混淆。
当您订阅一个 observable 时,大多数时候您只是传递一个回调函数,它描述了您希望如何在接收到流中的数据时对其进行处理。实际上,对于不同类型的事件,观察者中可以包含 3 种不同的回调。他们是:
next - 当从流中接收到数据时调用。因此,如果您请求获取一些口袋妖怪统计数据,它将 调用“下一个”回调函数并将该数据作为 输入。大多数时候,这是您唯一关心的数据,并且 rxjs 的创造者知道这一点,所以如果你只包含 1 个回调 function into a subscription,订阅将默认为 将“下一个”数据传入此回调。
error - 不言自明。如果在您的 observable 中抛出错误但未被捕获,它将调用此回调。
- complete - 当 observable 完成时调用。
如果你想处理从一个可观察对象发出的所有不同类型的数据,你可以在你的订阅中编写一个观察者,看起来像这样:
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 */}
})
同样,这在大多数情况下是不必要的,但是当我们在 Observable 上调用“.toPromise()”方法时,理解这些类型的事件是必不可少的。当我们将 Observable 转换为 Promise 时,一旦 Observable 上的“Complete”方法被调用,Promise 将使用从 Observable 发出的最后一个“下一个”数据来解析。这意味着如果不调用“完成”回调,Promise 将无限期挂起。
是的,我知道你在想什么:我一直将我的 http 请求从 Observables 转换为 Promise,而且我从来没有 运行 遇到我的 Promise 无限期挂起的情况。这是因为一旦从 http 调用接收到所有数据,angular http 库就会调用 Observable 上的“完成”回调。这是有道理的,因为一旦您从请求中收到所有数据,您就完成了。您不会期望将来有更多数据。
这与您调用 firestore 的问题中描述的情况不同,我从经验中知道它使用套接字来传输信息,而不是 http 请求。这意味着通过连接,您可能会收到一组初始数据……然后是更多数据……然后是更多数据。它本质上是一个没有明确结束的流,所以它永远没有理由调用“完成”回调。行为和重播主题也会发生同样的事情。
为了避免这个问题,你需要强制 Observable 调用“Complete”回调,方法是通过管道传入“first()”或“take(1)”,它们会做同样的事情,调用“next”回调函数以初始数据集作为输入,然后调用“完成”回调。
希望这对外面的人有用,因为这个问题让我困惑了很长时间。
如果您仍然感到困惑,这个视频也是一个很好的参考:https://www.youtube.com/watch?v=Tux1nhBPl_w
如果您必须在 getUserData 方法中使用 .subscription,那么这是另一种方式。
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 =>{
});
}