2017-08-19 3 views
1

Ich bin auf der Suche nach der besten Möglichkeit zur Implementierung einer Optimierung für sehr teure Methode, die mehrere Parameter und gibt eine Observable zurück. Gibt es eine elegante Art, es zu tun?Memoize für Observables in Typescript

Was ich suche ist hübschere Version davon:

class Example { 

constructor(
     private databaseService: DatabaseService, 
     private someService: SomeService) 

expensive(param1: string, param2: string) : Observable<string> { 
    if (isMemoraized(param1,param2) { 
     return Observable.create(observer=> 
     observer.next(memorizedValue(param1, param2)); 
     observer.complete(); 
    } else { 
     return Observable.create(observer=>{ 
      Observable.forkJoin([ 
      this.databaseService.getValue(param1, param2), 
      this.someService.fetchDataFromServer(param2)].subscribe( 
      results => { 
     let result = results[0] + ' ' + results[1]; 
     memorizeValue([param1,param2], result); 
     observer.next(result); 
     observer.complete(); 
     }); 
     }); 
    } 
} 
} 

jede Hilfe dankbar!

+0

Der Dienst sollte wahrscheinlich Cache-Speicher anstelle von grundlegenden Memoization implementieren. Sollten die Ergebnisse nie ablaufen? Wie viele Anfragen sollen gespeichert werden? Was passiert, wenn eine Fehlerreaktion ausgelöst wird? – estus

+0

Ja - Ich bevorzuge Cache-Speicher über grundlegende Memoization und vorzugsweise ein Paket, das alles einschließlich Ablaufzeiten, Methoden zum Löschen des Cache und so weiter haben wird ... In Bezug auf die Anzahl möglicherweise Hunderte. – karruma

+0

Ich würde dann etwas wie https://github.com/jmdobry/CacheFactory vorschlagen. Wie beim Cacheschlüssel könnte es etwas wie 'JSON.stringify ([param1, param2])' sein. – estus

Antwort

1

Es gibt eine Reihe von Memo-Paketen, die auf NPM verfügbar sind. Für TypeScript würde ich typescript-memoize empfehlen, das Ihnen einen Dekorator zur Verfügung stellt, mit dem Sie Ihre Methoden erstellen können.

Zum Beispiel:

import {Memoize} from 'typescript-memoize'; 

class Example { 

    @Memoize((param1: string, param2: string) => { 
     return param1 + ';' + param2; 
    }) 
    expensive(param1: string, param2: string) : Observable<string> { 
     // ... 
    } 
} 
+0

Dies scheint nicht zu funktionieren, wenn ein zurückgegebener Typ Observable ist. Ich habe einige Tests geschrieben und es funktioniert nicht. – karruma

+0

Vielleicht wäre es besser, die teuren Operationen (d. H. DB-Abruf) in Hilfsfunktionen zu trennen, die Sie memoisieren können? – fny

1

Wenn Sie nicht bereit, jede Bibliothek zu verwenden und Ihren eigenen Code zu schreiben.

expensive(param1: string, param2: string) : Observable<string> { 
    return isMemoraized(param1, param2) 
     ? Observable.of(memorizedValue(param1, param2)) 
     : Observable.forkJoin([ 
      this.databaseService.getValue(param1, param2), 
      this.someService.fetchDataFromServer(param2) 
      ]) 
      .map(results => results[0] +results[1]) 
      .do(memorizeValue(result); 

} 
1

Sie localStorage verwenden könnte zu halten, die Ergebnisse Ihrer teure Operation, indiziert durch einen Hash der beiden Parameter: Ich kann Ihren Code dieses Refactoring. Meine Lösung implementiert auch den Ablauf, um die Verwendung veralteter Ergebnisse zu vermeiden.

/** 
* Gets the key to be used to store result. Each key should be unique 
* to the parameters supplied, 
* and the same parameters should always yield the same key 
* @return {string} 
*/ 
getKey(param1, param2){ 
    return `${param1}__${param2}`; 
} 

/** 
* Stores results in localStorage and sets expiration date into the future 
*/ 
store(param1, param2, result, secsToExpire){ 
    let toStore = { 
    data: result, 
    expires: Math.floor(Date.now()/1000) + secsToExpire 
    }; 
    localStorage.setItem(this.getKey(param1,param2), JSON.stringify(toStore)); 
} 

/** 
* Gets result from storage. If result is stale (expired) or unavailable, 
* returns NULL 
* @return {string|null} 
*/ 
retrieve(param1, param2){ 
    let result = localStorage.getItem(getKey(param1,param2)); 
    if(!result==null) result = JSON.parse(result); 
    if(result==null || result.expires < Math.floor(Date.now()/1000)){ 
    return null; 
    } 
    return result.data; 
} 

/** 
* Gets result from localStorage if available. Else, fetch from server 
* and store before returning an Observable that will emit the result 
* @return {Observable<string>} 
*/ 
expensive(param1, param2):Observable<string>{ 
    let result = this.retrieve(param1,param2); 
    if(result) return Observable.of(result); 

    // zip will match up outputs into an array 
    return Observable.zip(
    this.databaseService.getValue(param1, param2), 
    this.someService.fetchDataFromServer(param2) 
) // take ensures completion after 1 result. 
    .take(1).map(e => e[0] + ' ' + e[1]) 
    // store in localStorage 
    .do(res => this.store(param1,param2, res)) 
} 
Verwandte Themen