【问题标题】:How to correctly retrieve and cache data in Angular service如何在 Angular 服务中正确检索和缓存数据
【发布时间】:2020-02-12 22:36:56
【问题描述】:

通常,在 Angular 应用程序中,我有一些服务,需要通过 http 请求检索一些数据并通过 BehaviorSubject 将其共享给消费者。它有这样的实现:

class Service { 

  private data = new BehaviorSubject();

  getData() {
    if (!this.data.getValue()) {
      anyHttpCall().subscribe(res => this.data.next(res));
    }

    return this.data.asObservable();
  } 
}

这种方法的主要问题是当应用程序的某些组件在还没有值的情况下同时调用getData()时,会触发多次http调用和数据发射,所以我找到了2种方法来防止它:

1)存储表示请求状态的布尔变量

class Service { 

  private data = new BehaviorSubject();   

  private pendingResult = false;

  getData() {
    if (!this.data.value && !this.pendingResult) {
      this.pendingResult = true;
      anyHttpCall().subscribe(res =>   {
        this.data.next(res);
        this.pendingResult = false;
      }
    }

    return this.data.asObservable();
  } 
}

2)在服务构造函数中获取数据

class Service { 

  private data = new BehaviorSubject();  

  constructor() {
    anyHttpCall().subscribe(resthis.data.next(res));
  }

  getData() {
    return this.data.asObservable();
  } 
}

那么哪种方法或其他方法是最好的以及为什么

【问题讨论】:

标签: angular


【解决方案1】:

最好的方法是使用 rxjs shareReplay。这个操作符返回一个 Observable,它共享一个对底层源的订阅。换句话说,让我们的 observable 变热。

const CACHE_SIZE = 1;
class Service { 

  private _data$: Observable<YourType>;

  get data(): Observable<YourType> {
    if (!this._data$) {
      this._data$ = anyHttpCall()
        .pipe(shareReplay({ refCount: true, bufferSize: CACHE_SIZE })
      );
    }
    return this._data$;
  }
}

bufferSize 决定了重放缓冲区的最大元素数,即为每个订阅者缓存和重放的元素数。

这篇文章很好地解释了这一点:https://blog.thoughtram.io/angular/2018/03/05/advanced-caching-with-rxjs.html

【讨论】:

    【解决方案2】:

    如果在构造函数中初始化一个通过对getData 的初始调用而变热的流呢?然后第一个结果缓存在ReplaySubject

    class Service {
    
      private init = new Subject();
    
      private data = new ReplaySubject(1);
    
      constructor() {
        this.init.pipe(
          take(1),
          switchMap(() => anyHttpCall())
        )
        .subscribe(res => this.data.next(res));   
      }
    
      getData() {
        this.init.next();
    
        return this.data.asObservable();   
      } 
    }
    

    【讨论】:

      【解决方案3】:

      你的选择呢:

      1) 应该可以,但对我来说它需要大量的手写代码。

      2) 假设用户不会调用getData 方法,但您已经发送了冗余请求。


      有一个非常方便的运算符shareReplay 可以帮助您将冷的可观察到的热。

      import { Observable } from 'rxjs';
      import { shareReplay } from 'rxjs/operators';
      
      
      export class Service {
        private cache$: Observable<any>;
        ...
        getData() {
          if (!this.cache$) {
            this.cache$ = this.anyHttpCall().pipe(
              shareReplay(1)
            );
          }
      
          return this.cache$;
        }
      }
      

      Ng-run Example

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-05-17
        • 2017-11-22
        • 2021-10-18
        • 1970-01-01
        • 2014-09-25
        • 2013-10-28
        • 1970-01-01
        • 2011-03-26
        相关资源
        最近更新 更多