嵌套的可观测变量和混合承诺

时间:2019-05-08 11:45:06

标签: angular typescript promise observable

我有一个与Firestore后端通信的Angular 7应用程序。

由于异步处理,我仍然想将Promise vs Observables笼罩着。

请考虑以下情形。在加载页面时,我需要先检查一些用户权限,然后才能确定它可以显示给我们什么。

由于权限是动态的,并且基本上是页面级别的,因此我们需要获取当前页面的Firestore条目,获取登录的Firestore用户,我分别存储的扩展用户信息,权限名称Im寻找,最后,我可以检查页面用户权限中是否存在用户权限。

是的,我可能应该对数据进行非规范化。

但是您可以从中看到Im必须具有多个查询级别,在显示数据之前,我需要首先运行该查询,并且Im确实要厌倦所有嵌套的Observable订阅。

  checkGroupPermission(groupId: string, permissionName: string): any {
    this.userService.getCurrentUser().then(user => {
      this.userService.getUserForFirebaseUid(user.uid).subscribe(users => {
        if (users.length === 0) { return false; }
        this.groupUsersService.getAllForUser(users[0].id).subscribe(groupUsers => {
          const groupUser = groupUsers.find(cu => cu.groupId === groupId);
          this.roleService.getRole(groupUser.roleId).subscribe(role => {
            this.rolePermissionService.getRolePermissionsForRoleId(role.id).subscribe(rolePermissions => {
              this.permissionService.getPermissionByName(permissionName).subscribe(permissions => {
                if (permissions.length === 0) { return false; }
                const rolePermission = rolePermissions.find(rp => rp.permissionId === permissions[0].id);
                if (rolePermission) {
                  return true;
                } else {
                  return false;
                }
              });
            });
          });
        });
      });
    });
  }

我还拥有大约五个不同的权限来检查。我想整理一下,然后看一下Promises vs Observables。

checkSomething(): Promise<boolean> {
  return this.myService.getData().then(data => {
    return true;
  }, reason => {
    return false;
  });
}

subCheckOfSomething(): Observable<boolean> {
  return this.myService.getFirebaseSubscribe().pipe(map(dataArray => {
    if (dataArray.length > 0) {
      return true;
    } else {
      return false;
    }
  }));
}

分别使用这两个函数是可以的,但是如果我尝试从另一个函数调用另一个函数:

checkSomething(): Promise<boolean> {
  return this.myService.getData().then(data => {
    this.subCheckOfSomething().subscribe(data => { return data; });
  }, reason => {
    return false;
  });
}

不起作用,因为可观察对象不是承诺

我知道我可以重写subCheckOfSomething来返回一个Promise:

subCheckOfSomething(): Promise<boolean> {
  return new Promise(resolve,reject) => {
  const data = this.myService.getFirebaseSubscribe().pipe(map(dataArray => {
    if (dataArray.length > 0) {
      return true;
    } else {
      return false;
    }
  }));
  data.subscribe(result => {
      resolve(result);
  }, err => {
      reject(err); })
  });
}

但这并不是最好的解决方案。有没有首选的方式来处理这种情况?

1 个答案:

答案 0 :(得分:1)

使用承诺

RxJs有一个toPromise方法,您可以用它来返回一个Promise。一旦可观察者完成,诺言将解析为可观察者的最后发射值。

subCheckOfSomething(): Promise<boolean> {
  return this.myService.getFirebaseSubscribe().pipe(map(dataArray => {
    if (dataArray.length > 0) {
      return true;
    } else {
      return false;
    }
  }))
  .toPromise();
}

使用观测值

问题是您不能及早打破可观察的链(您可以进行破解并抛出错误并在最后捕获该错误,但是我认为不建议这样做),因此您拥有这些嵌套的可观察订阅。正如您提到的,您应该将其整理成几种方法。您也只需要在最后订阅一次即可,就可以像这样通过管道传递其余方法:

  checkGroupPermission(groupId: string, permissionName: string): Observable<boolean> {
    this.userService.getCurrentUser().pipe(
      switchMap(user => {
      this.userService.getUserForFirebaseUid(user.uid).pipe(
        switchMap(users => 
          if (users.length === 0) { return of(false); }
          return this.groupUsersService.getAllForUser(users[0].id).pipe(
           switchMap(groupUsers => {
           // Basically you replace subscribe with pipe(switchMap())
        })
      })
    );
  }

我个人认为在您的应用程序的某些部分中做出承诺是可以的。对于我来说,async / await(不使用.then)似乎最容易理解,只要您不需要其他功能,例如“中止此检查并开始一个新的”(switchMap对此非常有用),我会满意的。此外,您可以使用toPromise(rxjs-> promise)或静态rxjs-operator from(promise-> rxjs)轻松地将彼此转移。