Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 28 additions & 8 deletions lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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) {
Expand All @@ -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]
}
}
}
}

Expand All @@ -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)
Expand All @@ -83,7 +103,7 @@ function observable (initialValue) {
*/

function canMutate () {
if (nextListeners === listeners) {
if (nextListeners !== undefined && nextListeners === listeners && !isFunction(nextListeners)) {
nextListeners = slice(listeners)
}
}
Expand Down
84 changes: 70 additions & 14 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand All @@ -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},
Expand All @@ -54,7 +63,7 @@ test('should allow to unsubscribe while listening', function (t) {
])
})
})
o.subscribe(record('C'))
o.subscribe(order.record('C'))
o(1)
})

Expand All @@ -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()
})