我有应用设置的单件服务
class Setting {
get foo() {
return storage.get('foo');
}
set foo(val)
storage.set('foo', val);
}
}
受到组件的约束'视图为setting.foo
。
由于storage
调用可能代价高昂且因为它可能是异步的,我宁愿用可以在需要时更新和读取storage
的RxJS主题替换getter / setter。
所以我将其重构为
class Setting {
constructor() {
this.fooSubject = new ReplaySubject(1);
fooSubject.subscribe((val) => {
storage.set('foo', val);
});
this.foo$ = this.fooSubject
.map(() => storage.get('foo'))
.publishReplay(1).refCount();
}
并使用setting.foo$ | async
和setting.fooSubject.next(newFoo)
。看起来现在缓存了昂贵的storage.get
次呼叫。
有两个问题。
第一个是fooSubject
主题和foo$
可观察者都可以公开获得这项工作,而选择一个主题是因为它应该是一个可观察者和一个观察者。 / p>
可以foo$
成为Setting
服务的单个主题属性,因此可以使用subscribe(...)
订阅并使用next(...)
进行更新吗?
第二个是代码仍然是同步的。
对于返回承诺的storage.get
和storage.set
,如何处理此案例?
答案 0 :(得分:3)
在您的代码中,当它实际上按您的需要工作时,没有太多建议。我不确定我是否完全了解您的用例,但我认为您可以完全不使用this.foo$
来简化您的代码。
存储最新值的功能由ReplaySubject
提供,除非您确实需要在storage.get('foo')
之后每次调用storage.set('foo', val);
,否则不需要。{/ p>
我将您的代码放入现场演示:http://plnkr.co/edit/pxjRQr6Q6Q7LzYb1oode?p=preview
所以我认为可以简化为此。
class Setting {
constructor() {
var storage = new Storage();
this.fooSubject = new ReplaySubject(1);
this.fooSubject.subscribe((val) => {
storage.set('foo', val);
});
}
get observable() {
return this.fooSubject.asObservable();
};
store(val) {
this.fooSubject.next(val);
}
}
我故意隐瞒这样一个事实:我使用Subject
.asObservable()
并使用.next()
方法调用store()
。典型用法可能如下:
let settings = new Setting();
settings.store('Hello');
settings.observable.subscribe(val => console.log(val));
settings.store('Hello 2');
settings.store('Hello 3');
settings.observable.subscribe(val => console.log(val));
settings.store('Hello 4');
打印到控制台:
Hello
Hello 2
Hello 3
Hello 3
Hello 4
Hello 4
请注意,您没有使用任何值初始化ReplaySubject
。即使您在创建setting.fooSubject.next(newFoo)
后立即致电ReplaySubject
,也会在订阅storage.set('foo', val);
后立即再次存储。{/ p>
关于同步的问题。那么,你的代码实际上是异步但是顺序的。由于JavaScript是单线程的,那么如果storage.get('foo')
执行一些同步耗时的操作,那么它将阻止执行线程,并且可能唯一的选择是将其移动到Web Worker。
答案 1 :(得分:3)
通过扩展AnonymousSubject
(Subject.create(...)
工厂的课程)来获得具有副作用的主题非常容易。结果主题获得包含原始主题和可观察的destination
和source
属性。
class FooSharedSubject extends AnonymousSubject {
constructor() {
const subject = new BehaviorSubject('');
const observable = subject.asObservable()
.mergeMap((value) => promisedStorage.get('foo'))
.publishReplay(1)
.refCount();
super(subject, observable);
}
next(value): void {
promisedStorage.set('foo', value)).then(
() => this.destination.next(value),
() => this.destination.error(value)
);
}
}