订阅承诺

时间:2019-04-09 18:49:33

标签: angular typescript promise rxjs angular-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抛出错误:

  

“订阅”类型的参数不能分配给类型的参数   '(解决:(值?:{} | PromiseLike <{}>)=>无效,拒绝:(原因?:   任何)=>无效)=>无效'。类型“订阅”没有匹配项   签名'(resolve:(value ?: {} | PromiseLike <{}>)=>无效,   reject:(reason ?: any)=> void):void'.ts(2345)

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

谢谢。

4 个答案:

答案 0 :(得分:1)

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(...)

答案 1 :(得分:0)

当前,您将返回整个订阅。要解决此问题,您需要使用toPromise

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

由于您在上面的fn中返回了承诺,因此无需创建新的Promise

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

答案 2 :(得分:0)

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 */}
})

在大多数情况下,这都是不必要的,但是当我们在Observable上调用“ .toPromise()”方法时,了解这些事件类型至关重要。当我们将Observable转换为Promise时,发生的情况是,一旦调用Observable上的“ Complete”方法,Promise将使用Observable发出的最后“下一个”数据进行解析。这意味着,如果未调用“ Complete”回调,则Promise将无限期挂起。

是的,我知道您在想什么:我一直将我的http请求从Observables转换为Promises,并且从未遇到无限期挂起Promise的情况。这是因为一旦从http调用接收到所有数据,Angular http库就会在Observable上调用“ Complete”回调。这是有道理的,因为一旦您收到了请求中的所有数据,您就完成了。您以后不会再有任何数据了。

这与您正在呼叫Firestore的问题中所描述的情况不同,据我所知,这是使用套接字传输信息而不是http请求的经验。这意味着通过连接,您可能会收到一组初始数据……然后是更多数据……然后是更多数据。本质上,这是没有确定终点的流,因此它永远没有理由调用“ Complete”回调。行为和重播主题也会发生同样的事情。

要避免此问题,您需要通过在“ first()”或“ take(1)”中进行配管来强制Observable调用“ Complete”回调,这将执行相同的操作,请调用“ next”回调函数以初始数据集作为输入,然后调用“完成”回调。

希望这对外面的人很有用,因为这个问题使我陷入困境的时间最长。

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

答案 3 :(得分:0)

如果您必须在 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 =>{

        });
}