如何在没有主题的情况下利用被动扩展来进行缓存?

Ove*_*d D 4 reactive-programming rxjs

我希望能够从外部Api获取特定请求的数据,但是当返回该数据时,也使其在缓存中可用,以表示应用程序的当前状态.

这个解决方案似乎有效:

var Rx = require('rx');

var cached_todos = new Rx.ReplaySubject(1);

var api = {
  refresh_and_get_todos: function() {
    var fetch_todos = Rx.Observable.fromCallback($.get('example.com/todos'));
    return fetch_todos()
      .tap(todos => cached_todos.onNext(todos));
  },
  current_todos: function() {
    return cached_todos;
  }
};
Run Code Online (Sandbox Code Playgroud)

但是 - 显然Subjects在Rx中是不好的做法,因为它们并不真正遵循功能性反应式编程.

以功能性反应式编程方式执行此操作的正确方法是什么?

pau*_*els 8

建议不要使用,Subjects因为有一种倾向滥用它们来注射副作用.它们完全有效,可以用作将值推送到流中的方法,但是它们的范围应该严格限制,以避免出血状态进入其他代码区域.

这是第一次重构,请注意您可以事先创建源代码,然后您的api代码只是将它包裹在一个整洁的小弓中:

var api = (function() {
    var fetch_todos = Rx.Observable.fromCallback($.get('example.com/todos'))
        source = new Rx.Subject(),
        cached_todos = source
          .flatMapLatest(function() { 
              return fetch_todos(); 
          })
          .replay(null, 1)
          .refCount();

    return {
      refresh: function() {
        source.onNext(null);
      },
      current_todos: function() {
        return cached_todos;
      }
    };
})();
Run Code Online (Sandbox Code Playgroud)

以上是好的,它保持你当前的界面和副作用和状态已被包含,但我们可以做得更好.我们可以创建一个扩展方法或一个接受一个的静态方法Observable.然后,我们可以进一步简化为:

//Executes the function and caches the last result every time source emits
Rx.Observable.withCache = function(fn, count) {
    return this.flatMapLatest(function() {
      return fn();
    })
    .replay(null, count || 1)
    .refCount();
};

//Later we would use it like so:

var todos = Rx.Observable.fromEvent(/*Button click or whatever*/))
             .withCache(
                 Rx.Observable.fromCallback($.get('example.com/todos')),
                 1 /*Cache size*/);


todos.subscribe(/*Update state*/);
Run Code Online (Sandbox Code Playgroud)