diff --git a/lib/index.js b/lib/index.js index 1de15ea..43f5b0e 100644 --- a/lib/index.js +++ b/lib/index.js @@ -17,8 +17,8 @@ module.exports = observable */ function observable (initialValue) { - var listeners = [] - var nextListeners = listeners + var listeners + var nextListeners var val = initialValue observableValue.subscribe = subscribe @@ -27,7 +27,9 @@ function observable (initialValue) { function observableValue (nextVal) { if (arguments.length > 0) { val = nextVal - next(val) + if (nextListeners !== undefined) { + next(val) + } } return val @@ -47,7 +49,13 @@ function observable (initialValue) { var isSubscribed = true canMutate() - nextListeners.push(listener) + if (nextListeners === undefined) { + nextListeners = listener + } else if (isFunction(nextListeners)) { + nextListeners = [nextListeners, listener] + } else { + nextListeners.push(listener) + } return function unsubscribe () { if (!isSubscribed) { @@ -57,8 +65,16 @@ function observable (initialValue) { isSubscribed = false canMutate() - var index = nextListeners.indexOf(listener) - nextListeners.splice(index, 1) + + if (nextListeners === listener) { + nextListeners = undefined + } else { + var index = nextListeners.indexOf(listener) + nextListeners.splice(index, 1) + if (nextListeners.length === 1) { + nextListeners = nextListeners[0] + } + } } } @@ -70,7 +86,11 @@ function observable (initialValue) { function next (val) { listeners = nextListeners - foreach(call, listeners) + if (Array.isArray(listeners)) { + foreach(call, listeners) + } else { + listeners(val) + } function call (listener) { listener(val) @@ -83,7 +103,7 @@ function observable (initialValue) { */ function canMutate () { - if (nextListeners === listeners) { + if (nextListeners !== undefined && nextListeners === listeners && !isFunction(nextListeners)) { nextListeners = slice(listeners) } } diff --git a/test/index.js b/test/index.js index beffed6..446daa1 100644 --- a/test/index.js +++ b/test/index.js @@ -5,6 +5,23 @@ var Observable = require('..') var test = require('tap').test +function noop () {} + +function recordOrder () { + var list = [] + return { + list: list, + record: function (name) { + return function (val) { + list.push({ + name: name, + val: val + }) + } + } + } +} + /** * Tests */ @@ -30,22 +47,14 @@ test('should pass new value to subscribers', function (t) { test('should allow to unsubscribe while listening', function (t) { t.plan(1) var o = Observable(0) - var order = [] - var record = function (name) { - return function (val) { - order.push({ - name: name, - val: val - }) - } - } - o.subscribe(record('A')) + var order = recordOrder() + o.subscribe(order.record('A')) var unsubscribe = o.subscribe(function (val) { - record('B')(val) + order.record('B')(val) unsubscribe() setImmediate(function () { o(2) - t.deepEqual(order, [ + t.deepEqual(order.list, [ {name: 'A', val: 1}, {name: 'B', val: 1}, {name: 'C', val: 1}, @@ -54,7 +63,7 @@ test('should allow to unsubscribe while listening', function (t) { ]) }) }) - o.subscribe(record('C')) + o.subscribe(order.record('C')) o(1) }) @@ -71,8 +80,55 @@ test('should throw an exception if something else but a function is added as lis test('repeated unsubscribtion should be okay', function (t) { var o = Observable(0) - var unsubscribe = o.subscribe(function () {}) + var unsubscribe = o.subscribe(noop) unsubscribe() unsubscribe() t.end() }) + +test('should work after listeners were drained and readded again', function (t) { + var o = Observable(0) + var unsubscribe = o.subscribe(noop) + o(1) + unsubscribe() + o(2) + unsubscribe = o.subscribe(noop) + o(3) + t.end() +}) + +test('should work with 0, 1 or many listeners', function (t) { + var o = Observable(0) + var unsub1 + var unsub2 + var order = recordOrder() + o(1) // zero listener + unsub1 = o.subscribe(order.record('A')) + o(2) // 1 listener + unsub2 = o.subscribe(order.record('B')) + o(3) // 2 listener + unsub1() + o(4) // 1 listener again + unsub2() + o(5) // 0 listener again + unsub1 = o.subscribe(order.record('C')) + o(6) + unsub2 = o.subscribe(order.record('D')) + o(7) + t.deepEqual(order.list, [ + {name: 'A', val: 2}, + {name: 'A', val: 3}, + {name: 'B', val: 3}, + {name: 'B', val: 4}, + {name: 'C', val: 6}, + {name: 'C', val: 7}, + {name: 'D', val: 7} + ]) + t.end() +}) + +test('it should work without subscriber', function (t) { + var o = Observable(0) + o(1) + t.end() +})