Comparing version 0.1.1 to 0.1.2
97
flyd.js
@@ -43,10 +43,11 @@ (function (root, factory) { | ||
var merge = curryN(2, function(s1, s2) { | ||
return stream([s1, s2], function(n, changed) { | ||
var v1, v2; | ||
if (changed) return changed(); | ||
else { | ||
v1 = s1(); v2 = s2(); | ||
return v1 === undefined ? v2 : v1; | ||
} | ||
var s = stream([s1, s2], function(n, changed) { | ||
return changed[0] ? changed[0]() | ||
: s1.hasVal ? s1() | ||
: s2(); | ||
}, true); | ||
endsOn(stream([s1.end, s2.end], function(self, changed) { | ||
return true; | ||
}), s); | ||
return s; | ||
}); | ||
@@ -73,5 +74,5 @@ | ||
function updateStream(s) { | ||
if (initialDepsNotMet(s)) return; | ||
if (initialDepsNotMet(s) || s.ended) return; | ||
inStream = s; | ||
var returnVal = s.fn(s, s.depChanged); | ||
var returnVal = s.fn(s, s.depsChanged); | ||
if (returnVal !== undefined) { | ||
@@ -81,3 +82,3 @@ s(returnVal); | ||
inStream = undefined; | ||
s.depChanged = undefined; | ||
s.depsChanged = []; | ||
} | ||
@@ -98,7 +99,11 @@ | ||
for (i = 0; i < s.listeners.length; ++i) { | ||
s.listeners[i].depChanged = s; | ||
findDeps(order, s.listeners[i]); | ||
if (s.listeners[i].end === s) { | ||
end(s.listeners[i]); | ||
} else { | ||
s.listeners[i].depsChanged.push(s); | ||
findDeps(order, s.listeners[i]); | ||
} | ||
} | ||
for (i = order.length - 1; i >= 0; --i) { | ||
if (!isUndefined(order[i].depChanged)) { | ||
if (order[i].depsChanged.length > 0) { | ||
updateStream(order[i]); | ||
@@ -116,11 +121,19 @@ } | ||
function destroy(stream) { | ||
if (stream.listeners.length !== 0) { | ||
throw new Error('Trying to destroy stream with listeners attached'); | ||
function end(s) { | ||
s.ended = true; | ||
if (s.deps) s.deps.forEach(function(dep) { removeListener(dep.listeners, s); }); | ||
} | ||
function endsOn(endS, s) { | ||
if (s.end) { | ||
removeListener(s.end.listeners, s); | ||
if (isUndefined(s.end.end)) end(s.end); | ||
} | ||
stream.deps.forEach(function(dep) { removeListener(dep.listeners, stream); }); | ||
s.end = endS; | ||
endS.listeners.push(s); | ||
return s; | ||
} | ||
function isStream(stream) { | ||
return isFunction(stream) && 'depsMet' in stream; | ||
return isFunction(stream) && 'hasVal' in stream; | ||
} | ||
@@ -132,3 +145,3 @@ | ||
function stream(arg, fn, waitForDeps) { | ||
function createStream() { | ||
function s(n) { | ||
@@ -147,3 +160,7 @@ if (arguments.length > 0) { | ||
for (var j = 0; j < s.listeners.length; ++j) { | ||
s.listeners[j].depChanged = s; | ||
if (s.listeners[j].end === s) { | ||
end(s.listeners[j]); | ||
} else { | ||
s.listeners[j].depsChanged.push(s); | ||
} | ||
} | ||
@@ -159,7 +176,5 @@ } | ||
s.listeners = []; | ||
s.deps = []; | ||
s.depsMet = isUndefined(waitForDeps) && arguments.length > 1 ? false : true; | ||
s.depChanged = undefined; | ||
s.queued = false; | ||
s.fn = fn; | ||
s.end = undefined; | ||
s.ended = false; | ||
@@ -171,11 +186,31 @@ s.map = map.bind(null, s); | ||
return s; | ||
} | ||
function createDependentStream(deps, fn, dontWaitForDeps) { | ||
var s = createStream(); | ||
s.fn = fn; | ||
s.deps = deps; | ||
s.depsMet = dontWaitForDeps; | ||
s.depsChanged = []; | ||
deps.forEach(function(dep) { | ||
dep.listeners.push(s); | ||
}); | ||
return s; | ||
} | ||
function stream(arg, fn, dontWaitForDeps) { | ||
var s, deps; | ||
if (arguments.length > 1) { | ||
s.deps = arg; | ||
arg.forEach(function(dep) { | ||
dep.listeners.push(s); | ||
}); | ||
deps = arg.filter(function(d) { return d !== undefined; }); | ||
s = createDependentStream(deps, fn, isUndefined(dontWaitForDeps) ? false : true); | ||
var depEndStreams = deps.filter(function(d) { return !isUndefined(d.end); }) | ||
.map(function(d) { return d.end; }); | ||
endsOn(createDependentStream(depEndStreams, function() { return true; }, true), s); | ||
updateStream(s); | ||
flushUpdate(); | ||
} else if (arguments.length === 1) { | ||
s(arg); | ||
} else { | ||
s = createStream(); | ||
endsOn(createStream(), s); | ||
if (arguments.length === 1) s(arg); | ||
} | ||
@@ -315,3 +350,3 @@ return s; | ||
scan: scan, | ||
destroy: destroy, | ||
endsOn: endsOn, | ||
map: curryN(2, function(f, s) { return map(s, f); }), | ||
@@ -318,0 +353,0 @@ curryN: curryN, |
{ | ||
"name": "flyd", | ||
"version": "0.1.1", | ||
"version": "0.1.2", | ||
"description": "The less is more, modular, functional reactive programming library", | ||
@@ -5,0 +5,0 @@ "main": "flyd.js", |
@@ -62,16 +62,2 @@ var assert = require('assert'); | ||
}); | ||
it('can specify dependencies manually', function() { | ||
var x = stream(3); | ||
var y = stream(4); | ||
var called = 0; | ||
var sum = stream([x], function(s) { | ||
called++; | ||
return x() + y.val; | ||
}); | ||
x(1)(2)(3); | ||
y(1)(2)(3); | ||
x(4)(5); | ||
assert.equal(called, 6); | ||
assert.equal(sum(), 8); | ||
}); | ||
it('is not called until dependencies have value', function() { | ||
@@ -108,30 +94,2 @@ var x = stream(); | ||
}); | ||
it('can destroy stream', function() { | ||
var x = stream(3); | ||
var y = stream(2); | ||
var sum = stream([y, x], function() { | ||
return y() * x(); | ||
}); | ||
assert.equal(y.listeners.length, 1); | ||
assert.equal(x.listeners.length, 1); | ||
flyd.destroy(sum); | ||
assert.equal(y.listeners.length, 0); | ||
assert.equal(x.listeners.length, 0); | ||
}); | ||
it('can not destroy stream with listeners', function() { | ||
var x = stream(3), thrown; | ||
var x2 = stream([x], function() { | ||
return x() * 2; | ||
}); | ||
var x4 = stream([x2], function() { | ||
return x2() * 2; | ||
}); | ||
assert.equal(x4(), 12); | ||
try { | ||
x2.destroy(); | ||
} catch(e) { | ||
thrown = true; | ||
} | ||
assert(thrown); | ||
}); | ||
it('can get its own value', function() { | ||
@@ -145,3 +103,3 @@ var num = stream(0); | ||
}); | ||
it('is called with changed stream', function() { | ||
it('is called with changed streams', function() { | ||
var s1 = stream(0); | ||
@@ -151,4 +109,4 @@ var s2 = stream(0); | ||
var dependend = stream([s1, s2], function(d, changed) { | ||
if (changed === s1) result.push(1); | ||
if (changed === s2) result.push(2); | ||
if (changed[0] === s1) result.push(1); | ||
if (changed[0] === s2) result.push(2); | ||
}); | ||
@@ -246,2 +204,68 @@ s1(1); | ||
}); | ||
describe('ending a stream', function() { | ||
it('works for streams without dependencies', function() { | ||
var s = stream(1); | ||
s.end(true); | ||
assert(s.ended); | ||
}); | ||
it('detaches it from dependencies', function() { | ||
var x = stream(3); | ||
var y = stream(2); | ||
var sum = stream([y, x], function() { | ||
return y() * x(); | ||
}); | ||
assert.equal(y.listeners.length, 1); | ||
assert.equal(x.listeners.length, 1); | ||
sum.end(true); | ||
assert.equal(y.listeners.length, 0); | ||
assert.equal(x.listeners.length, 0); | ||
assert(sum.ended); | ||
}); | ||
it('ends its dependents', function() { | ||
var x = stream(3); | ||
var y = stream([x], function() { | ||
return 2 * x(); | ||
}); | ||
var z = stream([y], function() { | ||
return 2 * y(); | ||
}); | ||
assert.equal(z(), x() * 2 * 2); | ||
x.end(true); | ||
assert(x.ended); | ||
assert.equal(x.listeners.length, 0); | ||
assert(y.ended); | ||
assert.equal(y.listeners.length, 0); | ||
assert(z.ended); | ||
}); | ||
it('updates children if stream ends after recieving value', function() { | ||
var x = stream(3); | ||
var whenX2 = stream([x], function() { if (x() === 0) return true; }); | ||
var y = stream([x], function(self) { | ||
return x(); | ||
}); | ||
flyd.endsOn(whenX2, y); | ||
var z = stream([y], function() { return y(); }); | ||
assert.equal(y(), z()); | ||
x(2); | ||
assert.equal(y(), z()); | ||
assert(!y.ended); | ||
assert(!z.ended); | ||
x(0); | ||
assert.equal(x.listeners.length, 1); | ||
assert(y.ended); | ||
assert.equal(y.listeners.length, 0); | ||
assert(z.ended); | ||
assert.equal(2, y()); | ||
assert.equal(2, z()); | ||
}); | ||
it('works if end stream has initial value', function() { | ||
var killer = stream(true); | ||
var x = stream(1); | ||
var y = flyd.endsOn(killer, stream([x], function(self) { | ||
return 2 * x(); | ||
})); | ||
x(2); | ||
assert.equal(2 * x(), y()); | ||
}); | ||
}); | ||
describe('promise integration', function() { | ||
@@ -360,2 +384,16 @@ it('pushes result of promise down the stream', function(done) { | ||
}); | ||
it('ends only when both merged streams have ended', function() { | ||
var result = []; | ||
var s1 = stream(); | ||
var s2 = stream(); | ||
s1and2 = flyd.merge(s1, s2); | ||
flyd.map(function(v) { result.push(v); }, s1and2); | ||
s1(12)(2); s2(4)(44); s1(1); | ||
s1.end(true); | ||
assert(!s1and2.ended); | ||
s2(12)(2); | ||
s2.end(true); | ||
assert(s1and2.ended); | ||
assert.deepEqual(result, [12, 2, 4, 44, 1, 12, 2]); | ||
}); | ||
}); | ||
@@ -570,3 +608,24 @@ describe('ap', function() { | ||
}); | ||
it('is called with all changed dependencies', function() { | ||
var result = []; | ||
var a = flyd.stream(0); | ||
var b = flyd.stream([a], function() { return a() + 1; }); | ||
var c = flyd.stream([a], function() { return a() + 2; }); | ||
var d = flyd.stream(0); | ||
var e = flyd.stream([d], function() { return d() + 4; }); | ||
var f = flyd.stream([d], function() { return d() + 5; }); | ||
var g = flyd.stream([d], function() { return d() + 6; }); | ||
var h = flyd.stream([a, b, c, d, e, f, g], function(self, changed) { | ||
var vals = changed.map(function(s) { return s(); }); | ||
result.push(vals); | ||
return 1; | ||
}); | ||
a(1); d(2); a(3); | ||
assert.deepEqual(result, [ | ||
[], [1, 3, 2], [2, 8, 7, 6], [3, 5, 4] | ||
]); | ||
}); | ||
}); | ||
}); |
102950
1932