【问题标题】:Subscribe http.post that is placed inside a promise Angular 6订阅放置在 Promise Angular 6 中的 http.post
【发布时间】:2019-03-18 14:09:44
【问题描述】:

当我将 promise 与 subscribe 和另一个异步任务混合在一起时,这对我来说变得很复杂。

这是我的身份验证服务:

getCurrentUserToken(){
    return new Promise((resolve,reject)=>{
      firebase.auth().currentUser.getIdToken(/* forceRefresh */ true).then(function(idToken) {
        resolve(idToken)
      }).catch(function(error) {
        reject(error)
      });
    }) 
  }

这是我的 HTTP 服务:

sendEmail(email) {

    return this.authService.getCurrentUserToken().then(token => {
      const httpOptions = {
        headers: new HttpHeaders({
          'Content-Type': 'application/json',
          'Authorization': 'Basic server-Password',
        })
      };
      let data = email
      data['idToken'] = token
      return this.http.post(this.apiServer + 'sendEmail', data, httpOptions)
    })

  }

这就是我在组件中调用sendEmail(email) 函数的方式:

    Observable.fromPromise(this.httpService.sendEmail(element)).subscribe(
      data3 => {
        console.log(data3)

      }, error => {
        console.log(error)
      }
    ))

我必须将 currentUserToken 传递给 API 以让 API 验证用户会话。尽管如此,getCurrentUserToken()sendEmail() 都在异步运行,所以我必须使用PromiseToken 传递给sendEmail() 函数,并让sendEmail 函数调用API 发送电子邮件.

没有承诺,我可以像这样订阅http.post

this.httpService.sendEmail(element).subscribe(
          data3 => {
            console.log(data3)

          }, error => {
            console.log(error)
          }
        ))

不幸的是,当我将承诺添加到其中时,我搞砸了,console.log 正在返回: Observable {_isScalar: false, source: Observable, operator: MapOperator}

请告知如何订阅位于Promise 内的http.post

【问题讨论】:

    标签: javascript angular rxjs firebase-authentication observable


    【解决方案1】:

    这里真的没有必要把事情复杂化。

    我将在这里使用async/await 语法,为此,我们必须使用Promises 而不是Observables。好消息是,我们可以利用 Observable 值上的 toPromise() 方法将其更改为 Promise

    在代码中也关注我的 cmets

    这是实现

    对于getCurrentUserToken

    getCurrentUserToken() {
      return firebase.auth().currentUser.getIdToken(true);
      // This will already return a Promise<string>
      // So no need to do a .then and then return from there.
    }
    

    对于sendEmail

    async sendEmail(email) {
      // Since getCurrentUserToken returns a Promise<string> we can await it
      const token = await this.authService.getCurrentUserToken();
      // token will now have the Current User Token
      const httpOptions = {
        headers: new HttpHeaders({
          'Content-Type': 'application/json',
          'Authorization': 'Basic server-Password',
        })
      };
      let data = email
      data['idToken'] = token
      return this.http.post(this.apiServer + 'sendEmail', data, httpOptions).toPromise();
      // Notice how we're calling the .toPromise() method here 
      // to change Observable into a Promise
    }
    

    如何使用?

    此代码将进入您之前调用this.httpService.sendEmail 的组件方法中。 请务必将该功能标记为 async THOUGH。

    // We can only await something in a function which is declared of type async
    async sendEmail() {
      try {
        const data = await this.httpService.sendEmail(element);
        // Since sendEmail again returns a Promise, I can await it.
        console.log(data);
      } catch (error) {
        console.log(error);
      }
    }
    

    【讨论】:

    • 感谢您的回答。但我在最后部分有一些问题。我是否必须用另一个异步代码包装整个 try catch 或者 try catchawait sendEmail() 的包装器?
    • 我已更新我的答案以澄清您的疑问。请看一看。
    【解决方案2】:

    我们为什么不在这里使用 Observable 而不是 Promises。

    getCurrentUserToken() { return new Observable(obs => { firebase .auth() .currentUser.getIdToken(/* forceRefresh */ true) .then(function(idToken) { obs.next(idToken); obs.complete(); }) .catch(function(error) { obs.error(error); }); }); } sendEmail(email): Observable { return new Observable(obs => { this.authService.getCurrentUserToken().subscribe(token => { const httpOptions = { headers: new HttpHeaders({ 'Content-Type': 'application/json', Authorization: 'Basic server-Password' }) }; let data = email; data['idToken'] = token; this.http .post(this.apiServer + 'sendEmail', data, httpOptions) .subscribe( result => { obs.next(result); obs.complete(); }, error => { obs.error(); } ); }); }); } // now call the service from Component like this. this.httpService.sendEmail(element).subscribe( data3 => { console.log(data3) }, error => { console.log(error) } ));

    【讨论】:

    • subscribe inside subscribe 是一种叫做“回调地狱”的反模式,你可以使用像switchMap/mergeMap 这样的扁平化操作符来解决这个问题
    • 您可能希望在需要两次或多次调用来解析一个对象的情况下使用 Promise。你不能对一部分数据进行操作,没有另一部分,因此你需要 promises 和 Promise.all。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-02-12
    • 1970-01-01
    • 2018-11-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多