函数在内部异步结果完成之前返回值

时间:2017-07-24 19:00:55

标签: javascript angular rxjs observable

我试图在我的服务中添加一个帮助方法,该方法将在同一个服务中调用另一个返回Observable的方法。辅助方法需要返回一个布尔值。问题是确定返回值是真还是假所需的逻辑是在订阅块内完成的,这显然是异步的。所以,即使价值是真的,我仍然总是得到一个假的回报。也许尝试混合这样的同步和异步编程并不是一个好方法。

在我的组件中,我尝试对服务进行以下调用:

this.isAdmin = this.teamsService.isAdmin(this.teamId, this.user['userId'])

服务:

getAssociations(id, postfix) {
  return this.httpService.get(this.base, id, postfix)
    .map((response) => {
      return this.responseHandler.handleData(response);
    })
    .catch((error) => {
      return this.responseHandler.handleError(error);
    }
  );
}

isAdmin(teamId, userId) {
  let isAdmin = false;

  this.getAssociations(teamId, '/teamMembers')
    .subscribe(
      _data => {
        if (_data['teamMembers']) {
          for (let i = 0; i < _data['teamMembers'].length; i++) {
            if (_data['teamMembers'][i]['id'] === userId) {
              isAdmin = true;
              break;
            }
          }
        }
      }
    );

  return isAdmin;
}

因此,isAdmin方法总是返回false,原因很明显。我最初通过执行以下操作来处理组件内部的逻辑:

private checkIsAdmin() {
  this.teamsService.getAssociations(this.teamId, '/teamMembers')
    .subscribe(
      _data => {
        if (_data['teamMembers'] && _data['teamMembers'].length) {
          _data['teamMembers'].map(member => {
            if (member['id'] === this.user['userId']) {
              this.isAdmin = true;
            }
          });
        }
        this.spinner.hide();
        this.loading = false;
      },
      _error => {
        this.spinner.hide();
      }
    );
} 

这很好用,直到我不得不在其他组件上重复相同的逻辑。所以我试图找到一种方法将该逻辑抽象到服务本身返回一个布尔值,因为这实际上是我需要的所有功能。

我尝试了一些想法,但没有一个有效。任何帮助都会很棒。感谢。

2 个答案:

答案 0 :(得分:0)

因为没有人告诉你原因,所以投票很糟糕。如果你不要求他们说出原因,那么给人们投票的能力似乎是不合逻辑的。您如何期望任何人都能学习这种方法。

无论如何我发布了一个解决方案。

我将组件中的调用更改为:

this.teamsService.isAdmin(this.teamId, this.user['userId'])
  .subscribe(response => {
    this.isAdmin = response;
  }
);

并将服务内的方法更新为:

isAdmin(teamId, userId) {
  return this.getAssociations(teamId, '/teamMembers')
    .map(_data => {
      let isAdmin = false;
      if (_data['teamMembers']) {
        for (let i = 0; i < _data['teamMembers'].length; i++) {
          if (_data['teamMembers'][i]['id'] === userId) {
            isAdmin = true;
            break;
          }
        }
      }
      return isAdmin;
    });
}

答案 1 :(得分:0)

您也可以将所有控制逻辑向上移动到Rx流中,如下所示:

isAdmin(teamId, userId) {
  return this.getAssociations(teamId, '/teamMembers')
    .mergeMap(data => data['teamMembers'] != null ? Rx.Observable.empty() : Rx.Observable.from(data['teamMembers']))
    .filter(user => user['id'] === userId)
    .mapTo(true) // found admin user
    .take(1)
    .concat(Rx.Observable.of(false));//default state; no admin user
}

根据getAssociations()的用法,您还可以将其返回类型签名从Observable<ResponseTeamWrapperSomething>更改为Observable<TeamMember>,这样您就可以在此函数中丢失mergeMap运算符。

通过移动此逻辑并使用平台提供的功能替换自定义控制流,可以减少代码正在执行的操作的认知过载。这也减轻了您的测试负担,因为您不必测试框架提供的逻辑。