组合/简化在多个函数中调用的两个promise

时间:2018-05-13 22:17:33

标签: angular typescript ionic-framework promise ionic3

我开始习惯于承诺驱动代码,但我发现自己经常重复代码。特别是,我有一个Ionic应用程序,我在那里进行许多API调用,在运行调用之前,我检查是否存在Internet连接以及用户是否经过身份验证。这些功能中的每一个都是这样构建的:

  noteCreate(): Promise<any> {
    return new Promise (resolve => {
      this.connCheck()
      .then(success => {
        if (success) {
          this.authCheck('Please login first!')
          .then(success => {
            if (success) {

              // API CALL

            }
          });
        }
      })
    });
  }

我希望有一种方法可以装饰我的API调用或将这两个检查浓缩成简单而不那么笨重/重复的东西,但我不确定我应该怎么做。

我查找connCheckauthCheck以返回true以继续API调用。我确实希望将这些功能分开,因为在某些情况下,我只需要检查连接,而在其他情况下只需要进行身份验证(本地)。

  connCheck(): Promise<boolean> {
    return new Promise(resolve => {
      this.http.get(this.myURL() + '/heartbeat/', {responseType: 'text'}).subscribe(
        (data) => {
          resolve(true);
        },
        (err) => {
          resolve(false);
        }
      );
    })
  }

  authCheck(message: string): Promise<boolean> {
    return this.storage.get('user').then((data) => {
      if (data) {
        return true;
      } else {
        return false;
      }
    });
  }

2 个答案:

答案 0 :(得分:2)

此代码包含反模式,有时称为nested promisees反模式和promise构造反模式(deferred antipattern的近亲)。

已经有了可以链接的承诺。无需创建另一个new Promise (resolve => ...)

承诺需要有限的嵌套级别。所有的承诺都应该被束缚。如果承诺只是闲逛并且没有被退回,这是一个错误。

如果有多个条件,可以通过拒绝承诺链来实现平衡:

  noteCreate(): Promise<any> {
    const alreadyAuthorized = new Error();

    return this.connCheck()
    .then(success => {
      if (success)
        return this.authCheck('Please login first!')
      else
        throw alreadyAuthorized ;
    })
    .then(success => {
       if (success) {
         return ... // API CALL
       }
    ))
    .catch(err => {
      if (err === alreadyAuthorized)
        return;
      else
        throw err;
    });
  }

更方便的方法是切换到async..await,因为它是为了提供承诺控制流而提供的语法糖。拒绝可以重写为从函数中提前返回:

  async noteCreate(): Promise<any> {
    if (!(await this.connCheck()))
      return;

    if (!(await this.authCheck('Please login first!')))
      return;

    return ... // API CALL        
  }

this.http.get(...).subscribe(...)与promises结合使用是一个错误。 Observable可以轻松转换为promises并返回:

  connCheck(): Promise<boolean> {
    return this.http.get(this.myURL() + '/heartbeat/', {responseType: 'text'})
    .map(Boolean) // maps data to a boolean
    .toPromise();
  }

答案 1 :(得分:1)

承诺的全部意义在于能够将逻辑链接起来。如果链条的任何部分失败,您可以在最后捕获它。你可以这样做:

return this.connCheck()
  .then(() => this.authCheck())
  .then(() => {
     ... API call...
  })
  .catch(error => ...)

如果任何承诺被拒绝,将触发捕获。

你应该把捕获物放在你的承诺链的最后。