在子链中拆分长管道链而不丢失有关子链之间传递的参数的信息

时间:2019-07-28 13:03:15

标签: rxjs

我在管道中的操作很长。该链的子部分代表某种高级操作。因此,例如,代码可能类似于

firstObservable().pipe(

  // FIRST high level operation
  map(param_1_1 => doStuff_1_1(param_1_1)),
  concatMap(param_1_2 => doStuff_1_2(param_1_2)),
  concatMap(param_1_3 => doStuff_1_3(param_1_3)),

  // SECOND high level operation
  map(param_2_1 => doStuff_2_1(param_2_1)),
  concatMap(param_2_2 => doStuff_2_2(param_2_2)),
  concatMap(param_2_3 => doStuff_2_3(param_2_3)),
)

为了提高代码的可读性,我可以如下重构上面的示例

firstObservable().pipe(
  performFirstOperation(),
  performSecondOperation(),
}

performFirstOperation() {
  return pipe(
      map(param_1_1 => doStuff_1_1(param_1_1)),
      concatMap(param_1_2 => doStuff_1_2(param_1_2)),
      concatMap(param_1_3 => doStuff_1_3(param_1_3)),
  )
}  

performSecondOperation() {
  return pipe(
      map(param_2_1 => doStuff_2_1(param_2_1)),
      concatMap(param_2_2 => doStuff_2_2(param_2_2)),
      concatMap(param_2_3 => doStuff_2_3(param_2_3)),
  )
}

现在,整个工作正常,我个人发现第二个版本中的代码更易读。不过我松散的是performFirstOperation()返回参数param_2_1的信息,然后performSecondOperation()使用该参数。

在不丢失从子管道到 sub-pipe 的参数信息的情况下,是否有其他策略可以打破长管道链?

2 个答案:

答案 0 :(得分:0)

在这里撇开forkJoin的不当用法,如果要保留该数据,则应进行一些不同的设置:

CREATE TABLE student (
    student_id INT auto_increment primary key,
    name VARCHAR(20),
    major VARCHAR(20)
);

INSERT INTO student (name, major)
    VALUES ('jack', 'biology');

通过这种方式,您已在高级运算符内部构建了第二条管道,以便可以保留第一组操作中的数据,并在第二组操作结束后使用内部映射来收集数据。 / p>

出于可读性方面的考虑,您可以执行类似的操作:

firstObservable().pipe(
  map(param_1_1 => doStuff_1_1(param_1_1)),
  swtichMap(param_1_2 => doStuff_1_2(param_1_2)),
  // forkJoin(param_1_3 => doStuff_1_3(param_1_3)), this isn't an operator
  concatMap(param_2_1 => {
    const param_2_2 = doStuff_2_1(param_2_1); // run this sync operation inside
    return doStuff_2_2(param_2_2).pipe(
      concatMap(param_2_3 => doStuff_2_3(param_2_3)),
      map(param_2_4 => ([param_2_1, param_2_4])) // add inner map to gather data
    );
  })
)

另一种解决方案将涉及多个订户:

firstObservable().pipe(
  performFirstOperation(),
  performSecondOperation(),
}

performFirstOperation() {
  return pipe(
    map(param_1_1 => doStuff_1_1(param_1_1)),
    swtichMap(param_1_2 => doStuff_1_2(param_1_2)),
    // forkJoin(param_1_3 => doStuff_1_3(param_1_3)), this isn't an operator
  )
}  

performSecondOperation() {
  return pipe(
    concatMap(param_2_1 => {
      const param_2_2 = doStuff_2_1(param_2_1);
      return doStuff_2_2(param_2_2).pipe(
        concatMap(param_2_3 => doStuff_2_3(param_2_3)),
        map(param_2_4 => ([param_2_1, param_2_4]))
      );
    })
  )
}

然后您可以独立订阅每个管道。

答案 1 :(得分:0)

我将一个复杂的操作分解为两个这样的内容:

主要代码

  dataForUser$ = this.userSelectedAction$
    .pipe(
      // Handle the case of no selection
      filter(userName => Boolean(userName)),
      // Get the user given the user name
      switchMap(userName =>
        this.performFirstOperation(userName)
          .pipe(
            switchMap(user => this.performSecondOperation(user))
          ))
    );

首次操作

  // Maps the data to the desired format
  performFirstOperation(userName: string): Observable<User> {
    return this.http.get<User[]>(`${this.userUrl}?username=${userName}`)
      .pipe(
        // The query returns an array of users, we only want the first one
        map(users => users[0])
      );
  }

第二操作

  // Merges with the other two streams
  performSecondOperation(user: User) {
    return forkJoin([
      this.http.get<ToDo[]>(`${this.todoUrl}?userId=${user.id}`),
      this.http.get<Post[]>(`${this.postUrl}?userId=${user.id}`)
    ])
      .pipe(
        // Map the data into the desired format for display
        map(([todos, posts]) => ({
          name: user.name,
          todos: todos,
          posts: posts
        }) as UserData)
      );
  }

请注意,我使用了另一个运算符(在这种情况下为switchMap)将值从一个运算符方法传递给另一个运算符。

我在这里闪耀:https://stackblitz.com/edit/angular-rxjs-passdata-deborahk