JS中的反应流如何工作?

时间:2016-03-05 05:03:56

标签: javascript reactive-programming

我是反应流中的新手,现在正试图理解它们。这个想法看起来非常简单明了,但在实践中我无法理解那里到底发生了什么。

现在我正在玩most.js,试图实现一个简单的调度程序。 scan方法似乎正是我所需要的。

我的代码:

var dispatch;
// expose method for pushing events to stream:
var events = require("most").create(add => dispatch = add); 
// initialize stream, so callback in `create` above is actually called
events.drain();

events.observe(v => console.log("new event", v));

dispatch(1);

var scaner = events.scan(
  (state, patch) => {
    console.log("scaner", patch);
    // update state here
    return state;
  },
  { foo: 0 }
);

scaner.observe(v => console.log("scaner state", v));

dispatch(2);

据我了解,第一个观察者应该被调用两次(每个事件一次),scaner回调和第二个观察者 - 每次一次(因为它们是在触发第一个事件后添加的)。

然而,在练习中,控制台会显示:

new event 1
new event 2
scaner state { foo: 0 }

无论我推进多少事件,都不会调用Scaner。

但是如果我删除第一个dispatch调用(在创建scaner之前),一切都按照我的预期运行。

这是为什么?我正在阅读文档,阅读文章,但到目前为止还没有发现任何与此问题相似的内容。我的建议在哪里错了?

2 个答案:

答案 0 :(得分:1)

最有可能的是,您已经从API

中学习了这样的示例
most.from(['a', 'b', 'c', 'd'])
    .scan(function(string, letter) {
         return string + letter;
        }, '')
    .forEach(console.log.bind(console));

他们建议像这样一步一步地执行:

  1. 获取数组['a', 'b', 'c', 'd']并将其值提供给流。
  2. Feed的值由scan()转换。
  3. ...并由forEach()消费。
  4. 但这并非完全正确。这就是您的代码无法运行的原因。

    most.js源代码中,您可以看到第1340行:

    exports.from = from;
    
    function from(a) {
        if(Array.isArray(a) || isArrayLike(a)) {
            return fromArray(a);
        }  
    ...
    

    因此from()转发给某些fromArray()。然后,fromArray()(代码中的下方)正在创建新的Stream

    ...
    function fromArray (a) {
        return new Stream(new ArraySource(a));
    }
    ...
    

    如果您继续,则会从Streamsink.event(0, array[i]);,其中0表示超时毫秒。代码中没有setTimeout,但如果您进一步搜索代码.event = function,您会发现许多其他代码可以揭示更多内容。特别是,在第4692行附近有Scheduler delay()和时间戳。

    总结一下:上面例子中的数组在一段时间后异步进入流中,即使时间似乎是0毫秒。

    这意味着您必须假设以某种方式首先构建流,然后使用。即使程序代码看起来不那样。但是,嘿,它始终是隐藏复杂性的目标:-)?

    现在您可以使用自己的代码进行检查。这是一个基于你的片段的小提琴:

    https://jsfiddle.net/aak18y0m/1/

    查看小提琴中的dispatch()来电。我用setTimeout()包裹了它们:

    setTimeout( function() { dispatch( 1 /* or 2 */); }, 0);
    

    通过这样做,我强制它们也是异步调用,就像示例中的数组值实际上一样。

    要运行小提琴,您需要打开浏览器调试器(查看控制台),然后按上面的运行按钮。控制台输出显示您的扫描仪现在被调用三次:

    doc ready
    (index):61 Most loaded: [object Object]
    (index):82 scanner state Object {foo: 0}
    (index):75 scanner! 1
    (index):82 scanner state Object {foo: 0}
    (index):75 scanner! 2
    (index):82 scanner state Object {foo: 0}
    

    首先是drain(),然后是每个事件。

    如果您在JavaScript能够构建整个流之后同步使用dispatch(),并在最后添加它们,那么您也可以获得有效的结果(但幕后不一样)。只需取消注释// Alternative solution之后的行,再次运行并观察结果。

答案 1 :(得分:0)

嗯,我的问题看起来并不像听起来那么笼统。它只是一个特定于lib的。

首先 - 从主题is not valid for most.js开始。他们主张“采取陈述性而非强制性的方法”。

第二 - 我尝试了jdk_home/src.zip lib,并且使用它来完成主题的代码。只是工作。更重要的是,Kefir.jsis explicitly recommended for Kefir.js中不支持相同的方法。

所以,问题在于特定的lib实现,而不是在我的脑海中。