Angular service that returns observables/subjects from a cache array to multiple instances of a component in realtime

前提是你 提交于 2019-12-22 11:32:05

问题


USE CASE

I have a content page that has multiple instances of UserActionsComponent. I also have a global DataService that acts as a data provider with (wannabe) advanced caching.

Data fetch hierarchy:

In-Memory Cache >> Browser Storage (async) >> HTTP Request

UserActionsComponent

Requests dataService.getData(URL,params,method)

PS: params has details like blogId which makes the hash signature unique

DataService (.getData())

1) Makes a unique hash KEY using something like genUniqueHash(URL+stringify(params)+method)

2) Checks if value is present in an in-memory cache (array[KEY]) - If yes, return ob$

3) Check if value is present in browser's storage (async; uses Ionic storage) - If yes, return ob$ else - save to in-memory cache, then return ob$(array[KEY])

4) Get the value from HTTP Call, save to Storage, save to in-memory cache and return ob$(array[KEY])

This concept is not working as I am not able to figure out how to return a shared observable (I'm guessing this should be a BehaviourSubject?) from an array of cache values. Please bring me on the right track.

NOTES

  • If user votes up in one component, it must reflect on all the others instances in view (all of that particular Blog Entry's UserActionsComponent should update)
  • There can be many UserActionsComponent in one view (Eg: For BlogEntry#1 and BlogEntry#25) on the same page!
  • The In-memory cache keeps growing as the user visits new Blog Posts (that's fine as it's a finite set)

I'm a noob to RxJS. I tried my luck with:

  • pipe(share()) [Since it's a service, the pipe was shared with all URLs]
  • returning of(cache[key]) [Didn't work as the last fetched BlogEntry's User Actions would reflect on all subscriptions]
  • returning the whole cache and then filtering out required data (using array[KEY]) at UserActionsComponent end (This is the current implementation)

The current implementation works, but it doesn't seem the best way to do it for this use case. Reason being: One view might have up-to 20 UACs [UAC-A(1), UAC-B(1), UAC(200) ...]. UAC emits next() twice. onInit of UAC and when user up-votes or down-votes in a UAC. Now since on change, we emit the cache data, an array of size 20 is emitted 2*20 times for this page view.

What would be the most optimized way to implement this using RxJS?


回答1:


You can only one Subject/Behaviour Subject. Return that as an observable which every UAC can subscribe to and listen for data. Now I am assuming there must be some unique identifier attached to every UAC, let's say a blog Id. So when you emit a data using Subject, along with data, append that blog Id as well in that data. In the UAC match if the UAC's id is the same as the id received from observable then update the data in UAC else ignore. This way the right UAC will be updated. For example:-

data.service.ts

const a = new BehaviourSubject();

getDataSource() {
 return a.asObservable():
}

cacheCheck(blogId: string) {
  // emit the data with a blogId.
  a.next({'id': blogId, 'data': getDataFromSomeWhere()})
}

blog.component.html

<app-uac [id]="blogId"></app-uac>

uac.component.ts

@Input id: string;
constructor(public dataService: DataService) {}
ngOnInit() {
   dataService.getDataSource().subscribe((data) => {
      if(data.id == this.id) //do somethin in UAC or emit event to parent
         //blog and let it do something
      else { //ignore}
   })
}

Something on this line.




回答2:


Current Implementation:

Writing it down as it would be helpful for someone in the future. I managed to optimize it using observable pattern this way:

  • UAC onInit() calls dataService.getData(url,params,method,guid)
  • The guid passed by the UAC is the cache key for the service
  • UAC has two subscriptions from dataService. One is dataService.getData method that returns an observable - mainly used as an initializer to set guid - it unsubscribes after getting its first value using .pipe(first()). Second subscription is to a BehaviourSubject called dataChanged. Whenever data is changed on dataService cache, it emits dataChanged.next(newDataDelta). This subscription persists till the component's onDestroy.
  • dataService checks hierarchically as cache[guid] > storage[guid] > http[url], sets data accordingly, and finally calls dataChangedSubj.next(cache[guid]) Only the changed delta would be emitted
  • All UACs receive the new cached DataSet (which is cache[guid]; contains only what was changed) and checks for receivedData[guid] to see if the emitted data is relevant to the UAC. If yes, it consumes it.
  • If user up-votes/down-votes on a UAC, UAC does necessary API calls, and on success, does dataService.updateData(guid,data), which in-turn emits the change using dataChanged.next(newDataDelta) to all UACs and the UACs that belong to the same blog post would update its data (as they would have the same guid)

Still open for the best way to implement this use case using RxJS.



来源:https://stackoverflow.com/questions/56428876/angular-service-that-returns-observables-subjects-from-a-cache-array-to-multiple

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!