我正在寻找以非常昂贵的方法实现优化的最佳方法,该方法需要多个参数并返回一个Observable。有一种优雅的方式吗?
我正在寻找的是更漂亮的版本:
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();
});
});
}
}
}
任何帮助表示赞赏!
答案 0 :(得分:1)
NPM上有许多便笺程序包。对于TypeScript,我推荐typescript-memoize,它将为您提供一个装饰器,您可以使用它来记忆您的方法。
例如:
import {Memoize} from 'typescript-memoize';
class Example {
@Memoize((param1: string, param2: string) => {
return param1 + ';' + param2;
})
expensive(param1: string, param2: string) : Observable<string> {
// ...
}
}
答案 1 :(得分:1)
如果您不愿意使用任何库并编写自己的代码。我可以将你的代码重构为:
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);
}
答案 2 :(得分:1)
您可以使用localStorage
来保存昂贵操作的结果,并通过两个参数的哈希进行索引。我的下面的解决方案也实现了到期以避免使用陈旧的结果。
/**
* 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))
}
答案 3 :(得分:1)
您可以创建一个装饰器以在运行时为每个装饰的函数记录结果:
function Memoized() {
return function(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const method = descriptor.value; // references the method being decorated
let cacheMember = propertyKey + "CacheMember";
// the Observable function
if (!descriptor.value) {
throw new Error("use MemoizeDecorator only on services methods");
}
descriptor.value = function(...args) {
if (!target[cacheMember]) {
let returnedObservable = method.apply(this, args);
if (!(returnedObservable instanceof Observable)) {
throw new Error(
`method decorated with Memoized Decorator must return Observable`
);
}
target[cacheMember] = returnedObservable.pipe(
publishReplay(),
refCount()
);
}
return target[cacheMember];
};
};
}
用法:
@Memoized()
expensive(param1: string, param2: string) : Observable<string> {
// ...the expensive task
}
警告! 装饰器是js的stage 2 proposal! 不要在未转译代码的情况下使用修饰符(Typescript完全支持它)