订阅承诺

Vla*_*iuk 7 promise rxjs typescript angular-promise angular

在我的 Angular 7 应用程序中,我有下一个功能:

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

Run Code Online (Sandbox Code Playgroud)

我想在另一个方法中使用这个函数:

   someMethod() {
      ...
      new Promise(this.getUserData(uid))
         .then(() => {...})
      ...
   }
Run Code Online (Sandbox Code Playgroud)

但我不能这样做,因为 TypeScript 会抛出一个错误:

'Subscription' 类型的参数不能分配给类型 '(resolve: (value?: {} | PromiseLike<{}>) => void, reject: (reason?: any) => void) => void' 类型的参数. 类型 'Subscription' 不匹配签名 '(resolve: (value?: {} | PromiseLike<{}>) => void, reject: (reason?: any) => void): void'.ts(2345 )

如何将getUserData()方法转换为承诺,或forJoin改为使用?

提前致谢。

Ale*_*ink 9

ggradnig 的实现是正确的解决方案,但是我想更深入地分析它为什么起作用,因此如果将来有人遇到这个问题,就不会感到困惑。

当您订阅一个 observable 时,大多数情况下您只会传入一个回调函数,该函数描述了您在收到数据时希望如何处理来自流的数据。实际上,对于不同类型的事件,观察者可以包含 3 种不同的回调。他们是:

  1. next - 从流接收数据时调用。因此,如果您请求获取一些 pokemon 统计信息,它将调用“next”回调函数并将该数据作为输入传入。大多数时候这是你唯一关心的数据,rxjs 的创建者知道这一点,所以如果你只在订阅中包含 1 个回调函数,订阅将默认将“下一个”数据传递到这个回调中。

  2. 错误- 不言自明。如果在您的 observable 中抛出错误但未被捕获,它将调用此回调。

  3. complete - 当 observable 完成时调用。

如果您想处理从 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 */}
})
Run Code Online (Sandbox Code Playgroud)

同样,这在大多数情况下是不必要的,但是当我们在 Observable 上调用“.toPromise()”方法时,理解这些类型的事件是必不可少的。当我们将 Observable 转换为 Promise 时,所发生的事情是,只要调用 Observable 上的“Complete”方法,Promise 就会使用 Observable 发出的最后一个“下一个”数据来解析。这意味着如果没有调用“Complete”回调,Promise 将无限期挂起。

是的,我知道你在想什么:我一直将我的 http 请求从 Observables 转换为 Promises,而且我从来没有遇到过我的 Promise 无限期挂起的情况。这是因为一旦从 http 调用中接收到所有数据,angular http 库就会在 Observable 上调用“Complete”回调。这是有道理的,因为一旦您从请求中收到所有数据,您就完成了。您不期望将来有更多数据。

这与您在调用 firestore 的问题中描述的这种情况不同,我从经验中知道它使用套接字来传输信息,而不是 http 请求。这意味着通过连接,您可能会收到一组初始数据……然后是更多数据……然后是更多数据。它本质上是一个没有明确结束的流,所以它永远没有理由调用“完成”回调。行为和回放主题也会发生同样的事情。

为了规避这个问题,你需要强制 Observable 通过管道“first()”或“take(1)”来调用“Complete”回调,这将做同样的事情,用初始值调用“next”回调函数一组数据作为输入,然后调用“Complete”回调。

希望这对那里的人有用,因为这个问题让我困惑了很长时间。

如果您仍然感到困惑,此视频也是一个很好的参考:https : //www.youtube.com/watch?v=Tux1nhBPl_w


小智 9

如果您必须在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);
            }
        });
    });
}
Run Code Online (Sandbox Code Playgroud)

那么你可以使用这样的

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

        });
}
Run Code Online (Sandbox Code Playgroud)


ggr*_*nig 6

subscribe将类型从 更改ObservableSubscription,从而导致类型错误。

您可能想要的是将 Observable 转换为 Promise,同时保留函数调用。您可以通过管道传输 Observabletap然后将结果转换为toPromise. 像这样:

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

确保创建一个完成管道,就像你可以用first操作符做的那样,否则 Promise 永远不会解析。

你可以new Promise(...)在你的消费者中省略。