读副作用与写副作用

目前为止,我们的副作用都被圈在了应用外部:

function main() {
  return {
    DOM: Rx.Observable.timer(0, 1000)
      .map(i => `Seconds Elapsed ${i}`),
    Log: Rx.Observable.timer(0, 2000)
      .map(i => 2 * i)
  };
}

function DOMDriver(text$) {
  text$.subscribe(text => {
    $app = document.querySelector('#app');
    $app.textContent = text;
  });
}

function consoleLogDriver(msg$) {
  msg$.subscribe(msg => {
    console.log(msg);
  });
}

更多时候,我们还需要从外部世界中获得副作用。例如,现在我们需要在点击页面时,重新开始计时,那么,需要完成两件事:

  • main() 能够接收页面的点击事件流。
  • DOMDriver() 能够输出页面的点击事件流。
function main(DOMSource) {
  const click$ = DOMSource;
  return {
    DOM: DOMSource.flatMapLatest(() =>
           Rx.Observable.timer(0, 1000)
            .map(i => `Seconds Elapsed ${i}`)
        ),
    Log: Rx.Observable.timer(0, 2000)
      .map(i => 2 * i)
  };
}

function DOMDriver(text$) {
  text$.subscribe(text => {
    $app = document.querySelector('#app');
    $app.textContent = text;
  });

  const DOMSource = Rx.Observable.fromEvent(document, 'click');
  return DOMSource;
}

function consoleLogDriver(msg$) {
  msg$.subscribe(msg => {
    console.log(msg);
  });
}

const drivers = {
  DOM: DOMDriver,
  Log: consoleLogDriver
};

function run(main, drivers) {
  const sink = main();
  Object.keys(drivers).forEach(key => {
    drivers[key](sink[key])
  });
}

run(main, drivers);

在这里,sink 表示了一个 写副作用 -- write effects,而 DOM source 则表示了一个 读副作用 -- read effetcs

目前,程序仍然难于运行,因为我们遇到一个 依赖环(Cycle)

const sink = main(DOMSource)
const DOMSource = DOMDriver(sink.DOM)

// DOMSource --> main
//    ^           |
//    |           v
// DOMDriver <-- sink

为了消除这个环,我们可以在 main()DOMDriver() 间增加一个 代理main() 从代理获取 DOMSource,而 DOMDriver() 则将输出交给代理。代理可以通过 Rx.Subject 实现,Subject 能向各个观察者广播新的值到来:

const proxyDOMSource = new Rx.Subject();
const sink = main(proxyDOMSource);
const DOMSource = DOMDriver(sink.DOM);
// 订阅 DOMSource,当 DOMSource 有值产生时,将值传递给 proxyDOMSource
DOMSource.subscribe(click$ => proxyDOMSource.onNext(click$));

查看示例

results matching ""

    No results matching ""