首页 文章

当没有结果返回时,如何防止Angular异步管道频繁进行服务器调用?

提问于
浏览
7

我在ngFor中使用 async 管道来观察Observable . Observable由访问我的服务器的服务创建,并且在枚举ngFor循环时的加载时间,服务正确地调用服务器 .

现在对于我不理解的部分:当任何结果回来时,一切都按预期发生 . 但是,如果服务器响应说,404,并且没有可用于枚举的结果,则 async 管道会导致服务继续触发请求,相隔毫秒 . 这显然不好 . 这里的设计期望是什么?如何优雅地处理在使用异步管道时返回错误的Observable?

在组件模板中:

<li *ngFor="let person of persons | async">{{person.id}}</li>

在Component主体中:

get persons: Observable<Person[]> {
    return this.personService.list();
}

在服务中:

list(): Observable<Person[]> {
    if (this.persons && this.persons.length) {
        return Observable.from([this.persons]);
    } else {
        return this.http.get('/person')
            .map((response: Response) => response.json())
            .map((data: Person[]) => {
                this.persons = data;
                return data;
            })
            .catch((error: any) => {
                let errMsg = "some error..."
                return Observable.throw(errMsg);
            });
        }
    }
}

2 回答

  • 6

    我有一个非常类似的问题,这是由于Angular2变化检测的工作原理 . 有多种解决方案对我有用 .

    保存对observable的引用,使其不会更改

    现在当你调用 persons getter时,它总是返回一个 ObservableObservable 实例(例如一个不同的对象),因此Angular会重新评估整个事物,并且在下一个更改检测周期中,它会再次执行,然后再次执行 . ..所以,这是如何解决的:

    @Component({
        selector: 'my-comp',
        template: `{{ _persons | async }}`,
    }) export class MyComponent implements OnInit {
        ngOnInit() {
            this._persons = this.personService.list();
        }
    }
    

    将ChangeDetectionStrategy更改为OnPush

    你可能想告诉Angular:“我知道我在做什么,我会在发生一些变化时告诉你自己”:

    import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from '@angular/core';
    
    @Component({
        changeDetection: ChangeDetectionStrategy.OnPush,
        selector: 'my-comp',
        template: `
          {{ persons | async }}
          <button (click)="reload">Reload</button>
        `,
    }) export class MyComponent implements OnInit {
        constructor(private cd: ChangeDetectorRef) {}
    
        ngOnInit() {
            this.persons = this.personService.list();
        }
    
        reload() {
            this.persons = this.personService.list();
            // this tells Angular that a change occurred
            this.cd.markForCheck();
        }
    }
    

    这两种解决方案都适用于我,我决定采用第二种方法,因为它也是一种优化

  • 0

    Angular会在偶数发生时检查更改 . 这意味着它将调用模板中存在的变量以查看其结果是否发生了变化 .

    如果在每次调用时返回不同的对象或数组(即使内容相同),angular将考虑更改内容并刷新模板(从异步管道重新订阅) .

    在你的情况下,你称之为“人”,每次不同的观察者都会返回 .

    一种解决方案是将observable影响到局部变量,然后在模板中使用此局部变量 . 像这样的东西:

    class myComponent {
    public $persons:Observable<Person[]>;
    constructor(){
    this.$persons = this.$getPersons();
    }
    
    private $getPersons():Observable<Person[]>{
    return this.personService.list();
    }
    }
    

    和你的模板

    <li *ngFor="let person of $persons | async">{{person.id}}</li>
    

    另一种方法是使用装饰器@Memoize,它将缓存方法的结果并为所有对象返回相同的对象 .

    https://www.npmjs.com/package/typescript-memoize

    保留你的代码,然后将装饰器添加到你的getter中:

    @Memoize()
    get persons: Observable<Person[]> {
        return this.personService.list();
    }
    

    额外奖励:您可能希望缓存从服务中收到的最后一个值(原因是您拥有"if condition") . 你应该考虑使用重播(publishLast or publishReplay

相关问题