JavaScript中的惰性事件流

时间:2014-02-02 17:48:23

标签: javascript functional-programming lazy-evaluation reactive-programming

我正在尝试模拟JavaScript中功能反应式编程领域的事件流。我基本上有两种方法可以做到这一点:

  1. 为每个事件流提供一个listeners数组,并允许外部代码订阅流中的事件。我不喜欢这种解决方案,因为它需要大量的簿记和可变状态。
  2. 在JavaScript中调整延迟评估和thunks以模拟延迟列表,允许您将事件流显示为传统事件侦听器生成的事件的惰性列表。没有簿记。
  3. 显然,第二种方法更为理想。因此,我刻意写了几个组合器,以便在JavaScript中进行延迟评估:

    function lazy(f, a) {
        return function (k) {
            return k(apply(f, a));
        };
    }
    
    function apply(f, a) {
        return f.apply(null, a);
    }
    

    lazy组合器接受一个函数和一个参数列表,并返回一个表示函数调用返回值的thunk。

    function eval(a) {
        return typeof a === "function" ? a(id) : a;
    }
    
    function id(a) {
        return a;
    }
    

    eval组合器接受thunk,对其进行求值并返回其值,否则返回一个未改变的急切值。它允许您同时处理惰性和急切值。

    接下来我写了一个cons函数来创建一个列表:

    function cons(head, tail) {
        return {
            head: head,
            tail: tail
        };
    }
    

    然后我写了一些实用函数来对测试进行惰性求值:

    function fib(a, b) {
        var c = a + b;
        return cons(c, lazy(fib, [b, c]));
    }
    
    function take(n, xs) {
        return xs && n > 0 ?
            cons(xs.head, lazy(take, [n - 1, eval(xs.tail)])) :
        null;
    }
    
    function toArray(xs) {
        return xs ?
            [xs.head].concat(toArray(eval(xs.tail))) :
        [];
    }
    

    最后我懒洋洋地得到了前10个斐波那契数字如下:

    var xs = take(10, fib(-1, 1));
    
    alert(JSON.stringify(toArray(xs))); // [0,1,1,2,3,5,8,13,21,34]
    

    它就像一个魅力:http://jsfiddle.net/Kc5P2/

    所以现在我尝试使用惰性列表在JavaScript中创建一个事件流构造函数。但是因为事件是异步生成的,我猜测我需要将所有函数转换为延续传递样式。

    问题的症结在于,如果thunk的值尚未可用,则无法推迟对thunk的评估。解决方案是创建异步eval函数,如下所示:

    function asyncEval(a, k) {
        typeof a === "function" ? a(k) : k(a);
    }
    

    现在,您可以从事件流构造函数返回异步thunk,如下所示:

    function getEventStream(type, target) {
        return function (k) {
            target.addEventListener(type, function listener(event) {
                target.removeEventListener(type, listener);
                k(cons(event, getEventStream(type, target)));
            });
        };
    }
    

    不幸的是,现在我们已经使它异步,我们还需要创建在惰性列表上运行的所有函数的异步版本(例如take)。我们不仅现在陷入了回调地狱,而且我们还需要每个函数的两个版本:同步和异步。

    您如何解决此问题?有没有其他方法可以在JavaScript中创建惰性事件流? BTW我已经使用订阅模型为JavaScript中的事件流创建了一个要点:https://gist.github.com/aaditmshah/8381875

2 个答案:

答案 0 :(得分:2)

我会使用promises,它非常适合表示同步和异步可用的值。

然后,流将是包含头部的结构的承诺,以及对尾部的承诺。您可能希望在https://stackoverflow.com/a/17414193/1048572查看我的演示实现。

答案 1 :(得分:0)

我去年在JavaScript中对流进行了一些研究:

What's the relationship between streamjs and linqjs

我结束了逆向工程流程并将其扩展到我的目的。