2016-09-29 61 views
4

結合共享RxJS受試者我有一個單獨服務用於應用設置用於雙向數據在角2

class Setting { 
    get foo() { 
    return storage.get('foo'); 
    } 

    set foo(val) 
    storage.set('foo', val); 
    } 
} 

了在組件的視圖綁定爲setting.foo

由於storage調用可能代價高昂,因爲它可能是異步的,所以我寧願用RxJS主題替換getter/setter,它可以在需要時更新並讀取storage

所以我就來重構

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$ | asyncsetting.fooSubject.next(newFoo)。它看起來像昂貴的storage.get調用現在被緩存。

有兩個問題。

第一個問題是fooSubject主題和foo$可見的應公開可用於做這項工作,而選擇一個主題,因爲它應該是一個可觀察的和觀察者。

可以foo$被做成Setting服務的單一主題屬性,因此它可以與subscribe(...)認購,並與next(...)更新?

第二個是代碼仍然是同步的。

如何處理storage.getstorage.set返回承諾?

回答

3

通過擴展AnonymousSubjectSubject.create(...)工廠的類別)可以很容易地獲得副作用。產生的主題獲得destinationsource屬性,該屬性保存原始主題並且可觀察。

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) 
     ); 
    } 
} 
3

當你的代碼實際工作時,你沒有太多的建議。我不確定我是否確切地瞭解了您的用例,但我認爲您可以通過完全不使用this.foo$來簡化您的代碼。

存儲最新值的功能由ReplaySubject提供,除非您確實需要在storage.set('foo', val);之後每次調用storage.get('foo'),則不需要。

我把你的代碼放到一個現場演示: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任何價值。即使你在創建ReplaySubject後立即調用setting.fooSubject.next(newFoo),它將在訂閱storage.set('foo', val);後再次存儲。

關於同步的問題。那麼,你的代碼實際上是異步的,但是是順序的。由於JavaScript是單線程的,因此如果storage.get('foo')執行了一些同步耗時的操作,那麼它將阻塞執行線程,並且可能唯一的選擇是將其移動到Web Worker。

+0

感謝您的回覆。目標是將'setting.foo'屬性暴露爲* single *可讀/可寫屬性(subject),而不是像現在這樣的getter和setter對('setting'上可能會有更多這樣的屬性)。我即將使用承諾不是因爲「存儲」耗時,而是因爲目前我使用同步API(localStorage),我期望它會切換到異步(localForage)。 – estus

+0

然後我認爲你可以把我的'Setting'版本作爲你的'Setting'中的一個項目用作'setting.foo',或者甚至可以創建你自己的Subject來擴展'ReplaySubject' :)。 – martin

+0

我對主題和子類化並不是很滿意,但這似乎是一個非常好的主意,所以謝謝。 – estus