我正在尝试模拟JavaScript中功能反应式编程领域的事件流。我基本上有两种方法可以做到这一点:
listeners
数组,并允许外部代码订阅流中的事件。我不喜欢这种解决方案,因为它需要大量的簿记和可变状态。显然,第二种方法更为理想。因此,我刻意写了几个组合器,以便在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
答案 0 :(得分:2)
我会使用promises,它非常适合表示同步和异步可用的值。
然后,流将是包含头部的结构的承诺,以及对尾部的承诺。您可能希望在https://stackoverflow.com/a/17414193/1048572查看我的演示实现。
答案 1 :(得分:0)