I have a singleton service for app settings
class Setting {
get foo() {
return storage.get(\'foo\');
}
set foo(val)
storage.set(\'foo\', val);
It was really easy to get a subject with side effects by extending AnonymousSubject
(a class for Subject.create(...)
factory). Resulting subject gets destination
and source
properties that hold original subject and observable.
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)
);
}
}
There's not much to suggest in your code when it actually works as you want. I'm not sure I catch your use case exactly but I think you can simplify your code by not using this.foo$
at all.
The functionality of storing the latest value is provided by ReplaySubject and unless you really need to call storage.get('foo')
every time after storage.set('foo', val);
it's not necessary.
I put your code into a live demo: http://plnkr.co/edit/pxjRQr6Q6Q7LzYb1oode?p=preview
So I think it can be simplified to this.
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);
}
}
I purposely hide the fact that I'm using a Subject
with .asObservable() and by wrapping .next()
call with store()
method. A typical usage could look like:
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');
Which prints to console:
Hello
Hello 2
Hello 3
Hello 3
Hello 4
Hello 4
Note, that you're not initializing the ReplaySubject
with any value. Even if you called setting.fooSubject.next(newFoo)
right after creating the ReplaySubject
it'll be stored again right after subscribing with storage.set('foo', val);
.
The issue about being synchronous. Well, your code is in fact asynchronous but sequential. Since JavaScript is single threaded then if storage.get('foo')
does some synchronous time consuming operation then it'll block the execution thread and probably the only option is to move it to a Web Worker.