RX JS Angular вызовите метод один раз и сохраните результат, верните сохраненные данные во все последующие запросы - PullRequest
3 голосов
/ 05 февраля 2020

Извлечение данных из справочных таблиц!

Если они уже получены, получить данные из localStorage или извлечь по службе и сохранить в localStorage.

Если предыдущий запрос уже обрабатывается, другой запрос должен ждать полные, так что они могут получить данные из localStorage

псевдокод:

isInProgress = false;
getLookup(lookupName: string) {
  if(this.isInProgress)
     // Code to wait, no delay or timer, please


  if(localStorage(lookupName)){
     return of(localStorage.get(lookupName));
  } else {
      this.isInProgress = true;
      let url = `..../getLooup?name= {lookupName}`;
      this.httpClient.get(url).subscribe(data => {
         // Some other code to process the logic
         let processedData = someProcess(data);
         localStorage.set(lookupName, processedData);
         this.isInProgress = false;
         return of(processedData)
      }, error => {
         this.isInProgress = false;
         // Log error and other code
      })
  }
}

Я не очень хорош в RX JS, может быть простое решение!

Если это будет называться так:

xvyService.getLookup('A').subscribe(.....);
xvyService.getLookup('A').subscribe(.....);
xvyService.getLookup('A').subscribe(.....);
xvyService.getLookup('A').subscribe(.....);

База данных не должна ударить более одного раза.

Ответы [ 4 ]

1 голос
/ 05 февраля 2020

shareReplay может использоваться для реализации простого кэша. Он подписывается на источник Observable один раз в первой подписке и воспроизводит значения для всех последующих подписок. Более поздние подписки «ждут» автоматически, если никакое значение не может быть воспроизведено немедленно.

Общие логи c

if value in local storage
  return from local storage
else if cache not initialized
  cache = apiCall.pipe(
    // do things on first call only
    shareReplay(1) // replay last value
    // do things on all calls
  )
endif
return cache

Демонстрация: https://stackblitz.com/edit/angular-cwbtyc?embed=1&file=src / app /app.component.ts

let cache$: Observable<any>;

getLookup(lookupName: string): Observable<any> {
  const fromStorage = localStorage.getItem("lookupName");
  if (fromStorage) {
    console.log("from local storage");
    return of(JSON.parse(fromStorage));
  } else if (!this.cache$) {
    console.log("initializing cache");
    this.cache$ = this.requestData().pipe(
      map(data => {
        console.log('doing stuff');
        const processedData = { ...data, a: 1 }
        localStorage.setItem("lookupName", JSON.stringify(processedData));
        return processedData
      }),
      shareReplay(1),
      // Move catchError above shareReplay if you want to replay the safe error value instead
      catchError(error => {
        // Log error and other code - return default value on error
        return of(null);
      })
      // Populate local storage on every call and not just the first one
      //tap(data => localStorage.setItem("lookupName", JSON.stringify(data)))
    );
  }
  return this.cache$;
}
0 голосов
/ 06 февраля 2020

возможно что-то вроде:

   import {of, NEVER} from 'rxjs'
   import {map, catchError, shareReplay} from 'rxjs/operators'



   private inProg$ = {} // request cache
   getLookup(lookupName: string) {
     const stored = localStorage.get(lookupName) // check storage
     if (stored)
       return of(stored)
     const inProg = this.inProg$[lookupName] // check request cache
     if (inProg)
       return inProg
     let url = `..../getLooup?name= {lookupName}`;
     const req$ = this.httpClient.get(url).pipe( // build request
       map(data => {
         // Some other code to process the logic
         let processedData = someProcess(data);
         localStorage.set(lookupName, processedData); // populate storage
         delete this.inProg$[lookUpName]; // empty request cache
         return processedData
       }), 
       catchError(error => { // handle errors
         delete this.inProg$[lookUpName];
         // Log error and other code
         return NEVER // empty the cache and never emit
       }),
       shareReplay(1) // share it
     )
     this.inProg$[lookUpName] = req$ // populate request cache
     return req$
   }
0 голосов
/ 05 февраля 2020

В одном месте вы возвращаете Observable - of(localStorage.get(lookupName)), в другом вы возвращаете Observable в подписке - return of(processedData). Это не работает, потому что в первом случае вы работаете синхронно, но в следующем вы работаете асинхронно.

Вы можете попробовать что-то вроде:

if (localStorage(lookupName)) {
  return of(localStorage.get(lookupName));
}       

return this.httpClient.get(url).pipe(
  take(1),
  map((data) => {
    let processedData = someProcess(data);
    localStorage.set(lookupName, processedData);
    return processedData
  }));

и

xvyService.getLookup('A').subscribe((data) => console.log(data));

HttpClient должен отменить запрос, поэтому this.isInProgress не нужно.

0 голосов
/ 05 февраля 2020
import { BehaviorSubject } from 'rxjs';
const subject = new BehaviorSubject(null);

function getData(){
  let item = localStorage.getItem("data");
  if(item){
    console.log("--second----");
    subject.next(item);
  }else{
    //httpcall 
    console.log("--first");
    localStorage.setItem("data","TEST DATA");
  }
  return subject;
}

getData().subscribe(res =>{
  console.log("Success -- ", res);
},err=>{
  console.log("Error");
});

getData().subscribe(res =>{
  console.log("Success -- ", res);
},err=>{
  console.log("Error");
});
...