【问题标题】:RxJS not correctly combing the result of a secondary http getRxJS 没有正确组合二次 http get 的结果
【发布时间】:2020-02-17 17:39:14
【问题描述】:

我有一个由三个单独的搜索 API 提供服务的搜索框;您可以输入字符串或数字。字符串值非常简单,只返回找到的结果——可以是无或最多十个。对于数字,要么没有结果,要么只有一个结果,但必须进行第二次 GET 才能完成结果。输入的去抖时间为 300 毫秒,在输入三个字符之前不应开始搜索。

我的问题是,如果是数字,则进行了第二次 http 调用,但从未在结果集中实际使用过。 MapId 函数需要做其他事情——promise 显然不是可行的方法。

  • 使用 forkJoin 代替 combineLatest 会更好吗?
  • 我尝试过 flatMap,但无法将它与 forkJoin 结合使用。
  • 我现在有点迷路了。结合 Angular 组件中的订阅等待,执行一次获取、检查结果并在需要时执行第二次获取应该不难。

Angular 组件文件:

searchTerm$ = new Subject<string>();
filterData$;

onFiltering(args: FilteringEventArgs): void {
    this.searchTerm$.next(args.text);
}

setupSearchDebouncer(): void {
    this.filterData$ = this.searchTerm$
        .pipe(
            debounceTime(300),
            switchMap(s =>
                iif(() => s.length >= 3,
                    this.searchService.find(s)
                        .pipe(
                            map((result) => {
                                return result.results
                            }),
                        ),
                    of<Result[]>([])
                ))
        );

    this.filterData$.subscribe(r => {
        this.filterArgs.updateData(r);
    });
}

以及 RxJS 服务文件:

find(searchQuery: string) {
    return combineLatest(
        this.apiService1.search(searchQuery).pipe(),
        this.apiService2.search(searchQuery).pipe(),
        this.apiService3.search(searchQuery).pipe()
    ).pipe(
        map(([stringResult, idResult1, idResult2]) => {
            if (idResult1.length > 0 || idResult2.length > 0) {
                return this.mapName(idResult1, idResult2);
            }
            else {
                return this.mapId(stringResult);
            }
        })
    );
}

mapId(stringResult): SearchResult {
    // do some mapping
    return result;
}

mapName(idResult1, idResult2): SearchResult {
    const result = new SearchResult();

    let id = undefined;
    if (idResult1 && idResult1.length > 0) {
        id = idResult1[0].Id;
    }
    else if (idResult2 && idResult2.length > 0) {
        id = idResult2[0].Id;
    }

    this.apiService.fetchName(id).toPromise().then(succes => {
        result.results = [{ name: succes[0].name, id: id }];
    });
    return result;
}

【问题讨论】:

  • 那么第二个请求是this.apiService.fetchName(id),您只想在某些条件下执行?
  • 是的,只有在 idResult1 或 idResult2 中有结果时才应使用 mapName(并导致第二次 http get)。 (修正了一个错字;混淆了 mapId 和 mapName,抱歉)。

标签: angular rxjs


【解决方案1】:

combineLatest 可能是这里的最佳选择,因为您希望在任何源发出时触发搜索。 forkJoin 将在所有源完成时仅发出一次,因此如果源是主题,它将不起作用。这取决于this.apiService1.search(searchQuery) 在里面做了什么。

因此,您只需确保始终从 mapNameof() 返回 Observable:

mapName(idResult1, idResult2): Observable<SearchResult> {
    const result = new SearchResult();
    ...

    if (id) { // or whatever condition you need
      return this.apiService.fetchName(id).pipe(
        map(succes => {
          result.results = [{ name: succes[0].name, id: id }];
          return result;
        }),
      );
    }
    return of(result);
}

然后在主链中你将使用mergeMap 而不是map 并再次确保你总是返回一个带有of() 的Observable:

mergeMap(([stringResult, idResult1, idResult2]) => {
  if (idResult1.length > 0 || idResult2.length > 0) {
     return this.mapName(idResult1, idResult2);
  } else {
     return of(this.mapId(stringResult));
  }
}),

【讨论】:

  • 所有 apiservice.search 和 apiservice.fetchName 调用都是 http.gets——它们都返回常规的 observables。将 if-then 构造更改为使用 observables,然后让 mergeMap 找出来是诀窍。像 iif() 或 defer() 这样的东西会是一个选项,而不是简单的 if/then 吗?正确保存它 RxJS-y。
  • 您可以使用iif() 代替if-else 语句(或三元运算符? ... : ...)但不能使用defer()(无论如何您都需要if)。我认为iif() 很少使用,因为使用普通的if-else 更明显。
猜你喜欢
  • 1970-01-01
  • 2014-05-09
  • 1970-01-01
  • 2022-01-25
  • 1970-01-01
  • 1970-01-01
  • 2016-06-11
  • 2020-05-01
  • 1970-01-01
相关资源
最近更新 更多