使用SwitchMap()处理取消先前的请求

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用SwitchMap()处理取消先前的请求相关的知识,希望对你有一定的参考价值。

我的Angular2应用程序中有一个功能,用户可以选择各种过滤器来过滤网格视图中返回的数据。这种获取和过滤数据的工作正常。但是,我需要找到一种方法 - 最好使用RxJS和switchMap()之类的东西来取消对过滤器选择进行更改时的请求 - 这样只有最近的请求才会通过网络传输。

我一直很难让这个工作。所以我首先需要确定我当前的配置是否实际上是一个RxJS可观察,然后,在哪里插入像switchMap()这样的运算符。

这就是我的代码:

private sendRequest = _.debounce((languageFilter, locationFilter, zipFilter, firstNameFilter, lastNameFilter, branchFilter) =>
{
    this.filtersService.getByFilters(
        this.page - 1, this.pagesize, this.currentStage, this.language = languageFilter, this.location = locationFilter,
        this.zipcode = zipFilter, this.firstName = firstNameFilter, this.lastName = lastNameFilter,
        this.branch = branchFilter,
        (resRecordsData) => {
            this.records = resRecordsData;
         });
 }, 200);

public onFilterReceived(values)
{
    let languageFilter = [];
    let locationFilter = [];
    let zipFilter = [];
    let firstNameFilter = [];
    let lastNameFilter = [];
    let branchFilter = [];

     this.route.params.subscribe(
         (params: any) => {
             this.page = params['page'];
             this.pn_zip_e = params['pn_zip.e'];
             this.pn_firstName_e = params['pn_firstName.e'];
             this.pn_lastName_e = params['pn_lastName.e'];
             this.pn_location_e = params['pn_location.e'];
             this.pn_branch_e = params['pn_branch.e'];
             this.pn_language_e = params['pn_language.e'];
         }
     );

     this.pn_language_e === "1" ? languageFilter = values['language'] : languageFilter = [];
     this.pn_location_e === "1" ? locationFilter = values['location'] : locationFilter = [];
     this.pn_zip_e === "1" ? zipFilter = values['zip'] : zipFilter = [];
     this.pn_firstName_e === "1" ? firstNameFilter = values['firstName'] : firstNameFilter = [];
     this.pn_lastName_e === "1" ? lastNameFilter = values['lastName'] : lastNameFilter = [];
     this.pn_branch_e === "1" ? branchFilter = values['branch'] : branchFilter = [];

     this.sendRequest(languageFilter, locationFilter, zipFilter, firstNameFilter, lastNameFilter, branchFilter);
};

从我的filtersService调用的getByFilters()函数如下所示:

public getByFilters(page, pagesize, stage?, language?, location?, zipcode?, firstName?, lastName?, branch?, fn?: any)
{
    return this.apiService.get({
      req: this.strReq, reqArgs: { page, pagesize, stage, language, location, zipcode, firstName, lastName, branch }, callback: fn });
}

而这又在我们的中央请求控制器服务(apiService)中调用GET请求,如下所示:

public get(args: {
    req: string,
    reqArgs?: any,
    reqBody?: any,
    callback?: IRequestCallback
}): Observable<Response>
{
    args['reqType'] = 'get';
    return this.runHttpRequest(args);
}

一旦我收到我的回复,我将其分配给“this.records”,然后在我的视图中使用它来迭代“this.records.data” - 这是一个数组,将我的记录打印到屏幕上。

所以从上面的代码中,这行是我得到响应并将其分配给“this.records”的地方:

  this.records = resRecordsData;

所以,我的第一个问题是,我如何确定我是否有一个RxJS可观察 - 然后如何使用像switchMap()这样的运算符来处理取消之前的过滤器请求?

我尝试过这个,但它不起作用。我的猜测是语法不正确:

private sendRequest = _.debounce((languageFilter, locationFilter, zipFilter, firstNameFilter, lastNameFilter, branchFilter) =>
{
    this.streamFiltersService.getByFilters(
        this.page - 1, this.pagesize, this.currentStage, this.language = languageFilter, this.location = locationFilter,
        this.zipcode = zipFilter, this.firstName = firstNameFilter, this.lastName = lastNameFilter,
        this.branch = branchFilter,
         (resRecordsData) => {
            resRecordsData.switchMap((res) => {
                this.records = res;
            });
            console.log('records: ', this.records);
         });
 }, 200);

首先,为了确保我正在吠叫正确的树,我想有办法确定我在这里的响应是否真的是一个RxJS可观察的。而且,如果没有,找到一种方法将其转换为一个,所以我可以使用它上面的switchMap()

答案

我为switchMap创建了真实案例,并使用reactiveForm创建了两个输入。这可以适应fromEvent(inputElement,'change')而不是this.inputElement.valueChanges

// We want combine of all lastest values.
combineLatest(
  // We merge default name with observable of name input.
  merge(
    of('default'),
    // From input we want to be inform after 200ms debounce and if they have change.
    this.name.valueChanges.pipe(debounceTime(200), distinctUntilChanged())
    ),
  // We merge default choice with observable of choice input.
  merge(
    of('value 1'),
    this.choice.valueChanges.pipe(debounceTime(200), distinctUntilChanged())

  )
).pipe(map(e => {
  // We construct the request payload
  return {
    name: e[0],
    choice: e[1]
  }
  // Ignore combined default value, ask api and subscribe to answer.
})).pipe(skip(1), switchMap(e => this.myApi(e))).subscribe(console.log);

为了确保理解每一步,我强烈建议您通过组合变量和每个订阅流的console.log进行拆分。示例:

输入可观察

this.name.valueChanges.pipe(debounceTime(200), distinctUntilChanged()).subscribe(console.log)

去抖动200ms后将输出当前输入值,如果值与先前发出的值相比有变化。

使用默认值输入observable

merge(
    of('default'),
    this.name.valueChanges.pipe(debounceTime(200), distinctUntilChanged())
),

我们将立即默认值与输入流上发出的未来值合并。

CombineLatest

从以前的组合合并,我们希望将来自每个输入的最新发射值组合为单个流。

地图

.pipe(map(e => {
  // We construct the request payload
  return {
    name: e[0],
    choice: e[1]
  }
  // Ignore combined default value, ask api and subscribe to answer.
})

因为combineLatest将创建作为参数提供的最新发射流数组。我们希望将此数组映射到api的实际有效负载对象。

switchMap

switchMap(e => this.myApi(e)))

您有自己的有效负载(通过先前的地图描述生成),您将其转换为新的可观察对象。现在您可以简单地订阅并且神奇地了解您将根据您的输入值集合回答您的API,并自动取消之前不再相关的请求。

live sample


根据你的路线Params,你可以做这样的事情。

  ngOnInit() {
    // From url params change, we map output with default value.
    const routeParams$ = this.route.params.pipe(map(params => {
      return {
        user: '',
        choice: '',
        ...params
      };
    }));
    // Uncomment me to see it in action.
    // routeParams$.subscribe(console.log);
    // Transform this payload to http request and subscribe to it.
    routeParams$.pipe(switchMap(e => this.myApi(e))).subscribe(console.log);
  }

Live sample

以上是关于使用SwitchMap()处理取消先前的请求的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 RxAlamofire 取消先前的请求?

使用 axios 和 vue.js 取消先前的请求

自动搜索时快速取消 URLSession

从同一客户端取消先前的 MongoDB 操作

如果收到新请求,如何取消先前的任务?

了解rxjs中的SwitchMap