【问题标题】:Angular 6 send request onceAngular 6发送请求一次
【发布时间】:2018-09-03 17:31:31
【问题描述】:

请帮忙解决问题

我有一个方法在服务中

public list(): Observable<ILanguage[]> {
        return Observable.create((observer: Observer<ILanguage[]>) => {
            if (this._languages && this._languages.length > 0) {
                observer.next(this._languages);
                observer.complete();
            } else {
                this._http.get<ILanguage[]>(this._constants.apiUrl + '/langs').subscribe((allLanguages: ILanguage[]) => {
                    this._languages = allLanguages;
                    observer.next(this._languages);
                    observer.complete();
                });
            }
        });
    }

如果方法调用发生延迟 - 一切正常,则请求发送一次

但是如果方法被调用,几乎同时 - 有几个请求

试图把启动标志,也没有工作

public list(): Observable<ILanguage[]> {
        return Observable.create((observer: Observer<ILanguage[]>) => {
            if (this._languages && this._languages.length > 0) {
                observer.next(this._languages);
                observer.complete();
            } else if (!this._requestIsSend) {
                this._requestIsSend = true;
                this._http.get<ILanguage[]>(this._constants.apiUrl + '/langs').subscribe((allLanguages: ILanguage[]) => {
                    this._languages = allLanguages;
                    observer.next(this._languages);
                    observer.complete();
                });
            }
        });
    }

【问题讨论】:

  • 你可以直接使用take方法指定1作为参数,它只会执行一次
  • 你的问题不清楚,服务什么时候出错?
  • 你能举个例子吗?
  • 在多个位置同时发生服务方法调用时无法正常工作
  • 这不是angularjs 代码。 angular angularjs 不是同一个框架。

标签: rxjs observable angular6


【解决方案1】:

您的问题有点神秘,但我认为您要问的是如何防止重复调用 HTTP API。在第一次 HTTP 调用完成之前,您可能在 list() observable 上订阅了多次。您的标志仅在第一次调用完成后阻止 API 调用,从而导致不必要的同时调用。

但不用担心。这个缓存需求已经用ReplaySubject 内置在 RxJS 中,所以你不需要自己实现它。 ReplaySubject 采用单个参数,即在订阅时应发送给所有订阅者的最后一个值的数量。只需使用 1 作为参数值,它就会缓存你的 observable 的最新值(我猜是 allLanguages

您的代码可以简化为:

private languages$: Observable;

public list(): Observable<ILanguage[]> {
    if (!this.languages$) {
        this.languages$ = new ReplaySubject(1);
        this._http.get<ILanguage[]>(this._constants.apiUrl + '/langs').subscribe((allLanguages: ILanguage[]) => {
            this.languages$.next(allLanguages);
        });
    }
    return this.languages$;
}

甚至还有一个涉及管道的更清洁、更简单的解决方案。有一个管道用于与多播主题共享可观察到的发射。对于缓存,您可以使用shareReplay(1) 管道,其行为与ReplaySubject(1) 完全相同。因此,您创建了一个可连接的 observable(is probably not necessary for you)。要从中创建一个普通的 observable,只需将其通过管道传输到 refCount()。结果是,像上面一样,一个 Observables 将最后一个缓存的值重新发送给迟到的订阅者。

private languages$: Observable;

public list(): Observable<ILanguage[]> {
    if (!this.languages$)
        this.languages$ =
            this._http.get<ILanguage[]>(this._constants.apiUrl + '/langs')
                .pipe(shareReplay(1))
                .pipe(refCount());

    return this.languages$;
}

【讨论】:

    【解决方案2】:

    从函数共享 Observable 有 2 个问题:
    - 每个函数调用都返回新的 Observable(记忆函数)
    - Observable 是cold(将 Observable 转换为 hot)

    import * as _memoize from 'lodash/memoize';
    import { shareReplay } from 'rxjs/operators';
    
    export function ShareReplayObservable(target, key, descriptor) {
      const originalMethod = descriptor.value;
      const getObservableFn = function (...args) {
        return originalMethod.apply(this, args).pipe(shareReplay());
      };
    
      descriptor.value = _memoize(getObservableFn);
    
      return descriptor;
    }
    
    
    @ShareReplayObservable
    public list(): Observable<ILanguage[]> {
            return this._http.get<ILanguage[]>(this._constants.apiUrl + '/langs');
        }
    

    【讨论】:

      猜你喜欢
      • 2023-03-16
      • 1970-01-01
      • 2019-04-08
      • 2019-04-22
      • 1970-01-01
      • 1970-01-01
      • 2019-03-29
      • 2018-12-22
      • 1970-01-01
      相关资源
      最近更新 更多