首页 > 解决方案 > 从 RXJS observable 获取中间数据的最佳方式

问题描述

我想调用第一个服务来查询一些数据(返回一个结果数组),然后再次调用第二个服务来为每个结果获取更多数据。每次服务调用的结果应合并并返回并显示在一个列表中。我想我理解那部分是如何工作的。

我不太确定的部分是要求能够在可用时立即显示第一次服务调用的结果。UI 应该在数据可用之前显示来自第二个服务调用的字段的加载指示符。

我唯一能想到的是在第一次服务调用和第二次服务调用之后添加“tap”rxjs 操作符,它会在新主题上发出事件,并且 UI 可以监听 _documents$ 主题以在第一次调用后显示数据,并在第二次调用后显示组合数据。

是否有处理这种情况的最佳实践?我是否错过了另一种模式或 rxjs 运算符,这会使这更简单?

  _documents$: BehaviorSubject<DocumentSearchSomething[]> = new BehaviorSubject([]);
  private documentSearchQuery$ = new Subject<string>();

  constructor(private store: Store, private http: HttpClient) {

    this.documentSearchQuery$.pipe(
      switchMap((query) => {
        return this.http.get<any[]>(`https://my_api.com​/service_1?q=${encodeURIComponent(query)}`)
          .pipe(
            tap(service_1_results => this._documents$.next(service_1_results)),
            switchMap(service_1_results => {
              // make call to service 2 to get more data for each result, pass in id of every result
              return this.http.post<any[]>(`https://my_api.com/service_2`, {
                ids: service_1_results.map(r => r.id)
              }).pipe(
                map(service_2_results => {
                  // combine service_1_results with service_2_results
                  const combined = service_1_results.map(r1 => {
                    const r2 = service_2_results.find(c => c.id === r1.id);
                    return {...r1, ...r2};
                  });
                  return combined;
                }),
                tap(combined_results => this._documents$.next(combined_results))
              );
            })
          );
      }),
    ).subscribe();
  }

标签: javascriptangularrxjs

解决方案


是的,您可以使用一个运算符来简化此操作。由于此管道设置了一个内部 observable(导致发出组合值),您可以使用startWith()它立即从内部 observable 发出服务一的值。

    _documents$: Observable<DocumentSearchSomething[]>;
private documentSearchQuery = new Subject<string>();

constructor(private store: Store, private http: HttpClient) {

  this._documents$ = this.documentSearchQuery.pipe(
    switchMap(query => this.http.get<any[]>(`https://my_api.com​/service_1?q=${encodeURIComponent(query)}`)),
    switchMap(serviceOne => this.HTMLOutputElement.post<any[]>(`https://my_api.com/service_2`, {
      ids: serviceOne.map(r => r.id)
    }).pipe(
      map(serviceTwo => serviceOne.map(r1 => {
        const r2 = serviceTwo.find(c => c.id === r1.id);
        return { ...r1, ...r2 };
      })),
      startWith(serviceOne)
    ))
  );

}

推荐阅读