2016-09-29 56 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