【问题标题】:Subscription to promise订阅承诺
【发布时间】:2019-08-31 03:17:35
【问题描述】:

在我的 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 会抛出错误:

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

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

提前致谢。

【问题讨论】:

    标签: angular typescript promise rxjs angular-promise


    【解决方案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(...)

    【讨论】:

    • 删除消费者中的new Promise 调用。
    • 谢谢,这是工作。但我必须删除new Promise(...),因为这是不需要的,不能使用它。
    【解决方案2】:

    ggradnig 的实现是正确的解决方案,但是我想对它的工作原理进行更深入的分析,这样如果将来有人遇到这个问题,就不会感到困惑。

    当您订阅一个 observable 时,大多数时候您只传递一个回调函数,该函数描述了当您收到流中的数据时您希望如何处理它。实际上,虽然有 3 种不同的回调可以包含在不同类型的事件的观察者中。它们是:

    1. next - 从流中接收到数据时调用。所以如果你请求获取一些口袋妖怪的数据,它会 调用“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 */}
    })
    

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

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

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

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

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

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

        目前您正在退回整个订阅。要解决这个问题,您需要使用 toPromise

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

        由于你在上面的 fn 中返回了一个 Promise,所以不需要创建一个新的 Promise

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

        【讨论】:

          猜你喜欢
          • 2017-09-21
          • 1970-01-01
          • 2017-07-29
          • 2017-12-12
          • 1970-01-01
          • 2019-11-12
          • 1970-01-01
          • 2017-10-13
          • 2021-03-03
          相关资源
          最近更新 更多