如何停止同时提交多个角度的多个请求?

时间:2018-02-23 16:43:34

标签: angular caching jasmine karma-jasmine

我正在使用Angular 5,我注意到很多重复的HTTP调用。在一个例子中,我得到一些参考数据,几乎不会改变。我试图尽可能多地重用这些调用,但似乎无法阻止正在进行的调用。我正在使用Jasmine测试来显示问题。

我试过import { Injectable } from "@angular/core"; import { TestBed } from "@angular/core/testing"; import { HttpClient } from "@angular/common/http"; import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing'; @Injectable() class ApiService { constructor(private readonly http: HttpClient) { } getValue = () => this.http.get("").map(x => x).shareReplay(1); } describe("ApiService", () => { let apiService: ApiService; let httpMock: HttpTestingController; beforeEach(() => { TestBed.configureTestingModule({ declarations: [], imports: [HttpClientTestingModule], providers: [ApiService] }).compileComponents(); apiService = TestBed.get(ApiService); httpMock = TestBed.get(HttpTestingController); }); it("getValue for api called twice makes one call", () => { apiService.getValue().subscribe(x => expect(x).toBe(1)); apiService.getValue().subscribe(x => expect(x).toBe(1)); httpMock.expectOne("").flush(1); // Error: Expected one matching request for criteria "Match URL: ", found 2 requests. }); });

import { Injectable } from "@angular/core";
import { TestBed } from "@angular/core/testing";
import { HttpClient } from "@angular/common/http";
import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
import { Observable } from "rxjs/Observable";
import { Subject } from "rxjs/Subject";
import { ReplaySubject } from "rxjs/ReplaySubject";
import "rxjs/add/operator/map"

declare type GetDataHandler<T> = () => Observable<T>;

export class Cacheable<T> {

    protected data: T;
    protected subjectData: Subject<T>;
    protected observableData: Observable<T>;
    public getHandler: GetDataHandler<T>;

    constructor() {
        this.subjectData = new ReplaySubject(1);
        this.observableData = this.subjectData.asObservable();
    }

    public getData(): Observable<T> {
        if (!this.getHandler) {
            throw new Error("getHandler is not defined");
        }
        if (!this.data) {
            this.getHandler().map((r: T) => {
                this.data = r;
                return r;
            }).subscribe(
                result => this.subjectData.next(result),
                err => this.subjectData.error(err)
            );
        }
        return this.observableData;
    }

    public resetCache(): void {
        this.data = null;
    }

    public refresh(): Observable<T> {
        this.resetCache();
        return this.getData();
    }

}

@Injectable()
class CacheableApiService {
    private serverList = new Cacheable<any>();

    constructor(private readonly http: HttpClient) {
        this.serverList.getHandler = () => this.http.get("").map(x => x).shareReplay(1);
    }

    getValue = () => this.serverList.getData();
}

describe("CacheableApiService",
    () => {
        let cacheableApiService: CacheableApiService;
        let httpMock: HttpTestingController;

        beforeEach(() => {
            TestBed.configureTestingModule({
                declarations: [],
                imports: [HttpClientTestingModule],
                providers: [CacheableApiService]
            }).compileComponents();

            cacheableApiService = TestBed.get(CacheableApiService);
            httpMock = TestBed.get(HttpTestingController);
        });

        it("getValue called twice makes one call",
            () => {
                cacheableApiService.getValue().subscribe(x => expect(x).toBe(1));
                cacheableApiService.getValue().subscribe(x => expect(x).toBe(1));

                httpMock.expectOne("").flush(1); // Error: Expected one matching request for criteria "Match URL: ", found 2 requests.
            });
    });

我尝试过缓存

desc "Update the deployed code."
 task :update_code
   execute "/usr/bin/git pull origin #{fetch(:release_path)}")
   end
 end

1 个答案:

答案 0 :(得分:1)

在这些情况下,您遇到的问题是您正在创建一个缓存的流两次

如果你把你的测试重写为这样,我希望它会过去。

it("getValue for api called twice makes one call",
        () => {
            const stream = apiService.getValue();
            stream.subscribe(x => expect(x).toBe(1));
            stream.subscribe(x => expect(x).toBe(1));

            httpMock.expectOne("").flush(1); // should work now
        });

如果希望方法在每次调用时返回相同的流,那么这是一个与流无关的问题。你只想记住这个功能 - 有很多方法可以做到这一点!这是一个完全天真的方法:

@Injectable()
class ApiService {
  private source: Observable<any>; // horrible typing, please don't do this for real
  constructor(private readonly http: HttpClient) {}

  getValue = () => {
    if ( !this.source ) {
      this.source = this.http.get("").map(x => x).shareReplay(1);
    }
    return this.source;
  }
}

您可能还有兴趣将流设置为属性并直接使用它,而不是尝试从方法返回它。由于流是“懒惰的”,因此您不需要将功能提供给您的消费者。