共享RxJS主题用于Angular 2中的双向数据绑定

时间:2016-09-29 22:53:29

标签: javascript angular typescript rxjs

我有应用设置的单件服务

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$ | asyncsetting.fooSubject.next(newFoo)。看起来现在缓存了昂贵的storage.get次呼叫。

有两个问题。

第一个是fooSubject主题和foo$可观察者都可以公开获得这项工作,而选择一个主题是因为它应该是一个可观察者和一个观察者。 / p>

可以foo$成为Setting服务的单个主题属性,因此可以使用subscribe(...)订阅并使用next(...)进行更新吗?

第二个是代码仍然是同步的。

对于返回承诺的storage.getstorage.set,如何处理此案例?

2 个答案:

答案 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)

通过扩展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)
        );
    }
}