RXJS:返回嵌套的可观察变量

时间:2020-02-27 15:03:59

标签: angular asynchronous rxjs

我正在尝试返回一个由User和Results组成的UserDetail对象,通过Account的accessToken检索User(通过单个异步调用检索所有User)。目前,我仍想弄清楚如何导航到detailcomponent时如何返回此UserDetail对象(我知道由于调用是异步的,因此无法从字面上返回该对象,但是我需要在我的对象中使用该对象组件)。

下面的代码给我以下错误:

类型'Observable'不可分配给类型 “可观察”

我已经尝试过使用管道和映射,因为我已经阅读了这是如何将异步调用“返回”到调用该函数的组件的方法。然后,该组件应该可以处理订阅,但是如果不创建错误,我什至无法做到这一点。


@Injectable({
  providedIn: 'root'
})
export class UserResolver implements Resolve<UserDetails> {
  constructor(
    private as: AccountService,
    private rs: ResultService,
    private auth: AuthService
  ) {}

  resolve(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<UserDetails> {
    return this.as.getAccount(route.params['id']).pipe(
      map((acc: Account) => {
        this.as.getUserDetails(acc.access_token).pipe(
          map((user: User) => {
            if (user != null) {
              this.rs.getResults(this.auth.token).pipe(
                map((res: Result[]) => {
                  const ud: UserDetails = {
                    user,
                    results: res.filter(x => x.userId === acc.uid)
                  };
                  return of(ud);
                })
              );
            }
          })
        );
      })
    );

2 个答案:

答案 0 :(得分:1)

尝试使用switchMap代替mapmap只是将一个值转换为另一个值,而switchMap则允许您切换到另一个可观察的值。

在不了解发生了什么的情况下,我想您想要这样的东西:

let user: User;
let account: Account;
return this.as.getAccount(route.params['id']).pipe(
  tap((acc: Account) => account = account),
  switchMap((acc: Account) => this.as.getUserDetails(acc.access_token)),
  // this filter will stop the next steps from running if the user is null
  // it will also mean a value isn't emitted
  // if you want to emit a null, you will need to modify the pipe
  filter((u: User) => u !== null),
  tap((u: User) => user = u),
  switchMap((u: User) => this.rs.getResults(this.auth.token))
  map((res: Result[]) => {
    const ud: UserDetails = {
      user,
      results: res.filter(x => x.userId === account.uid)
    };
    return ud;
  })
);

注意它不再像您那样缩进,而是一系列管道运算符。每当您要切换到新的可观察对象时,请使用switchMapconcatMap。我们仅使用map将最后一个可观察到的结果映射到我们要从函数返回的值。

每当需要将状态保存到管道中间时,我都会使用tap来将其分配给变量。

此外,您正在(可能)对this.rs.getResults(this.auth.token)进行冗余调用。调用不会根据id参数的变化而变化,因此您只需获取一次,然后在后续调用中从缓存中读取即可。

编辑:concatMap也是一个选项。它们略有不同。两者都足以满足此答案的目的-我将不对您的用例进行假设。

答案 1 :(得分:1)

首先,应尽可能避免嵌套管道,以便于维护。 而是结合可观察的。

但是对于您的问题:只需删除“ of(ud)”,然后在最后一张地图中返回ud。

可维护性提示:

我看到您有一个电话,不需要按顺序解决。


const userDetails$: Observable<Userdetails> = this.rs.getResults(this.auth.token)
                                         .pipe(map((res: Result[]) => {
                                             const ud: UserDetails = {
                                             user,
                                             results: res.filter(x => x.userId === acc.uid)
                                             };
                                              return ud;
                                             });

const userCheck$: Observable<User> = this.as.getAccount(route.params['id'])
                                     .pipe(map((acc: Account) => 
                                     this.as.getUserDetails(acc.access_token));

// Only when both values are available those observables will be ziped. 
// Only one subscription is needed on hte returned Obersvable 

return zip(userDetails$,userCheck$).pipe(map(([userDetails,user])) => {
                                   if (user != null) {
                                      return userDetails
                                  }else{
                                      // should probably emit a error
                                  }));