【问题标题】:Using RxJS to Send One Network Request使用 RxJS 发送一个网络请求
【发布时间】:2019-06-12 22:52:07
【问题描述】:

在我的 Angular 应用程序中,我有一个函数接收一些用户选择的过滤器,然后根据这些过滤器值返回过滤后的数据。如果没有应用过滤器,则返回未过滤的数据。

过滤器值通过 Angular 的 EventEmitter() 从一个组件传递到另一个组件,所以在接收组件模板中,我有这个:

<div class="page-content">
    <grid [records]="records"
        (sendFilterValues)="onFilterReceived($event)">
    </grid>
</div>

我遇到的问题(我已经解决了好几天了)是因为过滤器是通过另一个组件的 EventEmitter() 进入的,这会导致接收这些选择的函数,然后使网络调用多次触发。

函数中间的console.log语句特别告诉我进入这个函数的过滤器导致它触发10次。

因此,对于用户来说,最终发生的事情是,他们在视图中看到数据发生了多次移动,因为多个调用发出 - 一些用于未过滤的数据,一些用于过滤的数据。我想控制它,所以只发出一个网络请求。

所以我想要做的是,因为这是一个可观察的,是使用某种 RxJS 运算符来进行一次调用。但是,我似乎无法让它工作。据我所知,我申请的运营商没有任何效果。

这是我的代码:

import { last } from 'rxjs/operators';

public onFilterReceived(values)
{
    let filters = {};

    if (values) {
        filters = values;
    }

    this.route.params.subscribe(
        (params: any) => {
            this.page = params['page'];
        }
    );

    let fn = resRecordsData => {
        this.records = resRecordsData;
    };

    console.count('onFilterReceived() is firing'); // fires 10 times

    this.streamFiltersService.getByFilters(
        this.page - 1, this.pagesize, this.currentStage, this.language = filters['language'], this.location = filters['location'],
        this.zipcode = filters['zip'], this.firstName = filters['firstName'], this.lastName = filters['lastName'],
        this.branch = filters['branch'], fn).last;
} 

虽然我的组件中有导入,但我的 IDE 提示它从未真正读取过。但是,当我在下面的代码之后立即添加. 时,如下所示:

this.streamFiltersService.getByFilters(
    this.page - 1, this.pagesize, this.currentStage, this.language = filters['language'], this.location = filters['location'],
    this.zipcode = filters['zip'], this.firstName = filters['firstName'], this.lastName = filters['lastName'],
    this.branch = filters['branch'], fn).

...各种 RxJS 运算符显示为选项,包括 last 运算符。

顺便说一句,这 10 次触发会返回到原始过滤器组件,我从下面的 console.log 向我显示这会触发 10 次 - 并且这会传递给接收组件。如果有另一种方法可以控制它只发射一次,那也可能解决我的问题。这是代码:

async onFilterValuesReceived({ 'zip' : zipArray, 'firstName' : firstName,
'lastName' : lastName, 'language' : language, 'location' : location, 'branch' : branch })
{
    let filtersObj = { 'zip' : zipArray, 'firstName' : firstName,
    'lastName' : lastName, 'language' : language, 'location' : location, 'branch' : branch };

    this.sendFilterValues.emit(await filtersObj);

    console.count('sending these...'); // fires 10 times
}

那么,如果是这样的话,为什么这个操作符在这里不起作用呢?我是在使用错误的运算符,还是以错误的方式使用它?还是有其他问题?我得到与使用这些运算符有关的零错误,但我也没有看到它们对函数的执行有任何影响。

顺便说一下,服务层中网络调用的代码是这样的:

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 });
}

...它又从根服务层调用它:

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

API 服务级别的结构是否存在问题?我们在所有这种类型的 GET 请求中使用它。当我在这里尝试使用 takeLast 运算符时,我收到一个 TS 错误:

[ts] 类型 '(this: Observable, count: number) => Observable' 不可分配给类型“可观察”。财产 '_isScalar' 在类型 '(this: Observable, count: number) => Observable'。

我已经为这个问题绞尽脑汁好几天了,如果我能解决这个问题,我将不胜感激。

我现在也完全接受非 RxJS 选项,如果有其他方法可以解决这个问题。

【问题讨论】:

    标签: angular rxjs


    【解决方案1】:

    既然你说你对非 RxJS 选项持开放态度,我会提供debounce 供你考虑。简而言之,如果你对请求进行去抖动,它只会在事件停止触发后触发一次。

    有很多库允许这样做,但我将展示一个带有 lodash - https://lodash.com/docs/4.17.10#debounce 的示例

    import * as _ from "lodash";
    
    private sendRequest = _.debounce(() => {
       this.streamFiltersService.getByFilters(
            this.page - 1, this.pagesize, this.currentStage, this.language = filters['language'], this.location = filters['location'],
            this.zipcode = filters['zip'], this.firstName = filters['firstName'], this.lastName = filters['lastName'],
            this.branch = filters['branch'], 
            (resRecordsData) => {
               this.records = resRecordsData;
            })
    }, 200);
    
    public onFilterReceived(values)
    {
        let filters = {};
    
        if (values) {
            filters = values;
        }
    
        this.route.params.subscribe(
            (params: any) => {
                this.page = params['page'];
            }
        );
    
        console.count('onFilterReceived() is firing'); // fires 10 times
    
        this.sendRequest();
    } 
    

    自上次调用 sendRequest 以来,仅经过 200 毫秒后,请求才会真正执行。

    【讨论】:

    • 这看起来很有希望。我会试一试。谢谢。
    • 进行了一些编辑以将过滤器对象传递给 sendRequest() 函数,它就像一个魅力一样工作。非常感谢!
    • 同样可以在 observable 的管道中使用 debounceTime() 操作符来完成:this.streamFiltersService.getByFilters(this.page - 1, this.pagesize, ...).pipe(debounceTime(200))。订阅((resRecordsData) => { this.records = resRecordsData; });然后,你就不需要 lodash 库了。
    【解决方案2】:

    作为另一个答案提到,您需要去抖动。要使用 RxJS 完成此操作,请在 observable 的管道中使用 debounceTime() 运算符:

    this.streamFiltersService.getByFilters(
        this.page - 1,
        this.pagesize,
        ...
    ).pipe(debounceTime(200)).subscribe((resRecordsData) => this.records = resRecordsData ); 
    

    然后,您不需要任何额外的库。

    我还建议您使用异步管道,这样您就可以忽略退订以防止内存泄漏。

    this.records$ = this.streamFiltersService.getByFilters(
        this.page - 1,
        this.pagesize,
        ...
    ).pipe(debounceTime(200)); 
    
    <app-whatever-component [records]="records$ | async"><app-whatever-component>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-04-21
      • 1970-01-01
      • 1970-01-01
      • 2020-03-26
      • 2021-12-18
      • 2021-03-22
      • 2020-09-30
      • 2021-06-05
      相关资源
      最近更新 更多