使用Kefir

时间:2015-08-02 04:06:29

标签: javascript frp rxjs bacon.js kefir.js

在Kefir中创建属性的惯用方法是什么,它会响应多种事件类型而发生变化?

在我的项目中,我开始使用rxjs作为FRP风格的应用程序。在此应用程序中,我想订阅响应多个变量而更改的状态。这就是我的工作方式:

const subject = new BehaviorSubject([]);

addEvents
  .withLatestFrom(subject, (newItem, items) => items.concat(newItem))
  .subscribe(subject);

removeEvents
  .withLatestFrom(subject, (item, items) => {
    return items.filter(i => i !== item);
  })
  .subscribe(subject);

我可以说这可能不是最好的做法;它似乎不是惯用的,我也只是想到订阅一个观察者到多个来源是不正确的。

我决定尝试与RxJs不同的库有很多理由,现在我正在评估Kefir,它有很好的文档和据称更好的性能。但我发现更难以确定如何做我想做的事情,缺少丑陋的黑客,我必须检查事件类型:

kefir
  .merge(addEvents, removeEvents)
  .scan(
    (items, event) => {
      switch (event.type) {
        case 'add': return items.concat(event.item);
        case 'remove': return items.filter(i => i !== event.item);
      }
    },
    [])
  .toProperty();

我真的更喜欢必须为大量条件块使用不优雅的技术来处理一堆事件类型,以便创建更改流。

我不打算使用Bacon.js,但我确实看到它正是我所需要的:

Bacon.update([],
  [addEvents],    (items, evt) => items.concat(evt.item),
  [removeEvents], (items, evt) => items.filter(i => i !== evt.item));

对于Kefir及其标准操作符,是否有一种自然的方式来做这种事情,或者这是我最终必须自己实现的?

1 个答案:

答案 0 :(得分:4)

我想出了这种我并不感到兴奋的方法,但至少代码非常干净:

const items = function () {
  let items = [];  
  return kefir
    .merge([
      addEvents.map(item => items = items.concat(item)),
      removeEvents.map(item => items = items.filter(i => i !== item))
    ])
    .toProperty(() => items);
}();

我不喜欢这个事实是它改变了状态,但由于JavaScript是单线程的,而且我隐藏了这种状态,也许它并不是那么糟糕。

这是一个效用函数:

import kefir from 'kefir';

export default function dynamicValue(initValue, ...args) {
  let value = initValue;
  let streams = [];
  while (args.length) {
    let [source, xform, ...remaining] = args;
    streams.push(source.map(v => value = xform(value, v)));
    args = remaining;
  }

  return kefir.merge(streams).toProperty(() => value);
}

......像这样使用:

dynamicValue([],
  addEvents, (items, item) => items.concat(item),
  removeEvents, (items, item) => items.filter(i => i !== item));

<强>更新

使用dynamicValuemapmerge计算出实现scan的不同方式,而无需变异变量:

import kefir from 'kefir';

export default function transform(initValue, ...args) {
  let mutations = [];
  while (args.length) {
    let [source, calculateNewValue, ...newArgs] = args;
    mutations.push(source.map(e => ({ event: e, mutation: calculateNewValue })));
    args = newArgs;
  }

  return kefir
    .merge(mutations)
    .scan((prev, { event, mutation }) => mutation(prev, event), initValue);
}

基本上,它将每个事件与变异函数配对,合并这些对,然后扫描它们,将变异函数应用于原始值和事件。我不确定它是否真的更好,但它看起来更像&#34;功能正常。&#34;