【问题标题】:How to use exhaustMap correctly - How to wait for a service call return before sending another request如何正确使用排气映射 - 如何在发送另一个请求之前等待服务调用返回
【发布时间】:2020-06-29 14:25:36
【问题描述】:

我使用一个按钮来initialiseGrid()。在通话进行时不禁用按钮,我想等待第一个响应返回,然后触发第二个请求,或者(即使触发多个请求)按照请求发生的确切顺序处理响应。我实际上尝试使用一些 rxjs 运算符而不是 map,但我很确定我没有正确使用它们。

2020 年 7 月 20 日更新: 我想exhaustMap 是正确的操作符。我应该如何在queryfetch 方法中使用它?

请在下面找到我的代码:

APs.service.ts

public query(token: string, tableName: string, state: any): void {
    console.log("state in query", state);
    this.fetch(token, tableName, state)
        .subscribe((x: any) => {
            console.log("response data", this.data);
            super.next(x);
            this.isLoading = false;
            this.mediatorService.sendMessage("APsRefreshed");
        },
            (err: any) => {
                console.log("err", err);
                this.isLoading = false;
            }
        );
}

public fetch(token: string, tableName: string, state: any): Observable<any> {
    let queryStr = `${toODataString(state)}&$count=true`;
    queryStr = queryStr.replace(/('[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}')/ig, function (x) {
        return x.substring(1, x.length - 1);
    });
    queryStr = queryStr.replace(/substringof\((.+),(.*?)\)/, "contains($2,$1)");
    const regex = /T00:00:00\.000Z/gi;
    const noTimeZoneQueryStr = queryStr.replace(regex, '');

    let fetchCallResult = this.http
        .get(`${this.BASE_URL}` + `/` + token + `${tableName}&${noTimeZoneQueryStr}`)
        .pipe(
            map((response: any) => ((<any>this.data) = {
                data: response['value'],
                total: /*response['value'].length*/parseInt(response['@odata.count'], 10)
            }
            )),
        //tap(() => this.isLoading = false)
    );
    return fetchCallResult;
}

APs.component.ts

public initialiseGrid() {
    this.APsService.isLoading = true;
    this.gridViewAPs = this.APsService;
    this.APsService.query(this.token, this.tableName, this.state);
}

APs.template.html

<kendo-grid #gridView
            [data]="gridViewAPs | async"
            [loading]="APsService.isLoading"
            ...
>

提前谢谢你。

【问题讨论】:

  • 我在这里只能看到一个 http 调用,第二个调用应该在哪里?
  • 没错。我的意思是同一服务的第二次(第 3 次、第 4 次等)调用。

标签: angular rxjs angular7


【解决方案1】:

当我需要控制执行顺序时,特别是对于 http 调用,我使用更高阶的 Map 运算符之一

  1. concatMap(以串行方式进行调用)
  2. mergeMap(以并行方式进行调用)
  3. switchMap(进行调用,但如果有新请求进入,则忽略(取消订阅)之前的调用)
  4. exhaustMap(发出调用并忽略任何新请求,直到上一个调用返回)

以上内容的优秀资源可以在这里找到https://blog.angular-university.io/rxjs-higher-order-mapping/

这是您的代码的草稿,已重构为以串行方式调用(我假设您的按钮单击是一个流,而地图运算符则用于编写您的查询输入)。将 concatMap 替换为上述任何运算符,以获得您的 http 调用所需的顺序。

this.btnCLick$ // 1. That's your button click stream
      .pipe(
        map(() => ({token, tableName, state})), // 2. Map all your inputs into one input object from where ever you want
        concatMap(({token, tableName, state}) => { // 3. This operator will wait for the previous call to complete to make the next call (like a Queue)
          let queryStr = `${this.toODataString(state)}&$count=true`;
          queryStr = queryStr
            .replace(/('[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}')/ig, 
              function (x) {
                return x.substring(1, x.length - 1);
              }
            );
          queryStr = queryStr.replace(/substringof\((.+),(.*?)\)/, "contains($2,$1)");
          const regex = /T00:00:00\.000Z/gi;
          const noTimeZoneQueryStr = queryStr.replace(regex, '');

          return this.http // 4. This part will make the http calls based on your query
            .get(`${this.BASE_URL}` + `/` + token + `${tableName}&${noTimeZoneQueryStr}`)
            .pipe(
              map((response: any) => ({
                  data: response['value'],
                  total: /*response['value'].length*/parseInt(response['@odata.count'], 10)
                })
              ),
              //tap(() => this.isLoading = false)
            );
        })
      )
      .subscribe(({data, total}) => { // 5. ***When each http call comes back with a response, the subscription will be called
            console.log("response data", data);
            super.next({data, total});
            this.isLoading = false;
            this.mediatorService.sendMessage("APsRefreshed");
        },
            (err: any) => {
                console.log("err", err);
                this.isLoading = false;
            }
      );

有了实现,您不必担心禁用按钮...

【讨论】:

  • 感谢您的回答。我会试试的。该按钮位于另一个组件中。因此,我无法直接访问该按钮。如上所示,在 ngOnInit() 中调用了 initialiseGrid(),并将网格数据源绑定到服务。所以,我不能使用 this.btnCLick$.pipe
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-01
  • 1970-01-01
相关资源
最近更新 更多