diff --git a/doc/API.md b/doc/API.md index ff7ebdc..0fe9e86 100644 --- a/doc/API.md +++ b/doc/API.md @@ -62,18 +62,22 @@ Imagine you're at a party and your next conversation depends on whom you run into first: Alice, Bob, or Charlie. ```JavaScript -switch (await Channel.select(alice.shift(), bob.shift(), charlie.push(`Hi!`)) { +switch (await Channel.select( + alice.shift(), + bob.shift(), + charlie.push(`Hi!`) +)) { case alice: - console.log(`Alice said ${alice.value}.`) - break + console.log(`Alice said ${alice.value}.`); + break; case bob: - console.log(`Bob said ${bob.value}.`) - break + console.log(`Bob said ${bob.value}.`); + break; case charlie: - console.log(`I said "hi" to Charlie.`) - break + console.log(`I said "hi" to Charlie.`); + break; } ``` @@ -81,55 +85,55 @@ Be careful of unintended side effects, however. Even though only one value is pushed in the following example, the counter is incremented twice. ```JavaScript -let counter = 0 +let counter = 0; const increment = () => { - counter++ - return counter -} + counter++; + return counter; +}; -await Channel.select(alice.push(increment()), bob.push(increment())) -assert.equal(counter, 2) +await Channel.select(alice.push(increment()), bob.push(increment())); +assert.equal(counter, 2); ``` Sometimes you don't want to wait until a method completes. You can use a closed channel to return immediately even if no other channels are ready: ```JavaScript -const closed = Channel() -closed.close() +const closed = Channel(); +closed.close(); -switch (await Channel.select(alice.shift(), bob.shift(), closed.shift()) { +switch (await Channel.select(alice.shift(), bob.shift(), closed.shift())) { case alice: - console.log(`Alice said ${alice.value}.`) - break + console.log(`Alice said ${alice.value}.`); + break; case bob: - console.log(`Bob said ${bob.value}.`) - break + console.log(`Bob said ${bob.value}.`); + break; default: - console.log(`No one has anything to say yet.`) + console.log(`No one has anything to say yet.`); } ``` You can also arrange it so that the `select` completes within a timeout: ```JavaScript -const timeout = Channel() -setTimeout(timeout.close, 1000) +const timeout = Channel(); +setTimeout(timeout.close, 1000); -switch (await Channel.select(alice.shift(), bob.shift(), timeout.shift()) { +switch (await Channel.select(alice.shift(), bob.shift(), timeout.shift())) { case alice: - console.log(`Alice said ${alice.value}.`) - break + console.log(`Alice said ${alice.value}.`); + break; case bob: - console.log(`Bob said ${bob.value}.`) - break + console.log(`Bob said ${bob.value}.`); + break; default: - console.log(`I stopped listening after one second.`) + console.log(`I stopped listening after one second.`); } ``` @@ -198,15 +202,15 @@ argument. The promise returned by `forEach` resolves when the channel is closed: ```JavaScript -const toArray = async (channel) => { - const array = [] +const toArray = async channel => { + const array = []; - await channel.forEach((value) => { - array.push(value) - }) + await channel.forEach(value => { + array.push(value); + }); - return array -} + return array; +}; ``` If `callbackfn` is async then `forEach` will wait for it before iterating to the @@ -214,9 +218,9 @@ next value: ```JavaScript const pipe = async (source, sink) => { - await source.forEach(sink.push) - sink.close() -} + await source.forEach(sink.push); + sink.close(); +}; ``` ### join(separator) -> async String @@ -286,15 +290,15 @@ in either of the following two ways: ```JavaScript // method -channel.slice(10) +channel.slice(10); // function -Channel.slice(10, Infinity, channel) +Channel.slice(10, Infinity, channel); ``` You can also use partial application to pass the channel in later: ```JavaScript -const skipTen = Channel.slice(10, Infinity) -skipTen(channel) +const skipTen = Channel.slice(10, Infinity); +skipTen(channel); ``` diff --git a/lib/index.js b/lib/index.js index 4ddbb33..4525085 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,342 +1,332 @@ -'use strict' +"use strict"; // An order represents a pending push or shift. -const Order = (channel) => { - let order - const preonFulfilleds = [] +const Order = channel => { + let order; + const preonFulfilleds = []; const promise = new Promise((resolve, reject) => { order = { - resolve: (value) => { - preonFulfilleds.forEach((preonFulfilled) => { - preonFulfilled(value) - }) + resolve: value => { + preonFulfilleds.forEach(preonFulfilled => { + preonFulfilled(value); + }); - resolve(value) + resolve(value); }, reject - } - }) + }; + }); Object.assign(promise, { cancel: () => { - order.cancelled = true + order.cancelled = true; }, channel, - prethen: (onFulfilled) => { - preonFulfilleds.push(onFulfilled) + prethen: onFulfilled => { + preonFulfilleds.push(onFulfilled); } - }) + }); - return {order, promise} -} + return { order, promise }; +}; -const prototype = {} +const prototype = {}; -const Channel = function (bufferLength = 0) { - let buffered = 0 - let closed = false - let lastValue - let resolvedIndex = 0 - const pushes = [] - const shifts = [] +const Channel = function(bufferLength = 0) { + let buffered = 0; + let closed = false; + let lastValue; + let resolvedIndex = 0; + const pushes = []; + const shifts = []; // Process the push and shift queues like an order book, looking for matches. const processOrders = () => { - const index = {push: 0, shift: 0} + const index = { push: 0, shift: 0 }; // Match pushes and shifts. - while ((index.push < pushes.length) && (index.shift < shifts.length)) { - const push = pushes[index.push] - const shift = shifts[index.shift] + while (index.push < pushes.length && index.shift < shifts.length) { + const push = pushes[index.push]; + const shift = shifts[index.shift]; if (push.cancelled) { - index.push++ + index.push++; } else if (shift.cancelled) { - index.shift++ + index.shift++; } else { - lastValue = push.value - shift.resolve(lastValue) - buffered = Math.max(0, buffered - 1) - index.push++ - index.shift++ + lastValue = push.value; + shift.resolve(lastValue); + buffered = Math.max(0, buffered - 1); + index.push++; + index.shift++; } } // Resolve push promises up to the end of the buffer. for ( ; - (resolvedIndex < index.push) || - ((resolvedIndex < pushes.length) && (buffered < bufferLength)); + resolvedIndex < index.push || + (resolvedIndex < pushes.length && buffered < bufferLength); resolvedIndex++ ) { - const {cancelled, resolve} = pushes[resolvedIndex] + const { cancelled, resolve } = pushes[resolvedIndex]; if (!cancelled) { if (resolvedIndex > index.push) { - buffered++ + buffered++; } - resolve(bufferLength) + resolve(bufferLength); } } // If the channel is closed then resolve 'undefined' to remaining shifts. if (closed) { - for (; - index.shift < shifts.length; - index.shift++ - ) { - const {cancelled, resolve} = shifts[index.shift] + for (; index.shift < shifts.length; index.shift++) { + const { cancelled, resolve } = shifts[index.shift]; if (!cancelled) { - lastValue = undefined - resolve(lastValue) + lastValue = undefined; + resolve(lastValue); } } } - pushes.splice(0, index.push) - shifts.splice(0, index.shift) - resolvedIndex -= index.push - } + pushes.splice(0, index.push); + shifts.splice(0, index.shift); + resolvedIndex -= index.push; + }; const readOnly = Object.assign(Object.create(prototype), { every: async (callbackfn, thisArg) => { for (;;) { - const value = await readOnly.shift() + const value = await readOnly.shift(); if (value === undefined) { - return true + return true; } else { if (!callbackfn.call(thisArg, value)) { - return false + return false; } } } }, filter: (callbackfn, thisArg) => { - const output = Channel() - - ;(async () => { + const output = Channel(); + (async () => { for (;;) { - const value = await readOnly.shift() + const value = await readOnly.shift(); if (value === undefined) { - await output.close() - break + await output.close(); + break; } else if (callbackfn.call(thisArg, value)) { - await output.push(value) + await output.push(value); } } - })() + })(); - return output + return output; }, forEach: async (callbackfn, thisArg) => { for (;;) { - const value = await readOnly.shift() + const value = await readOnly.shift(); if (value === undefined) { - break + break; } else { - await callbackfn.call(thisArg, value) + await callbackfn.call(thisArg, value); } } }, - join: async (separator) => { - const elements = [] + join: async separator => { + const elements = []; - await readOnly.forEach((element) => { - elements.push(element) - }) + await readOnly.forEach(element => { + elements.push(element); + }); - return elements.join(separator) + return elements.join(separator); }, map: (callbackfn, thisArg) => { - const output = Channel() - - ;(async () => { - await readOnly.forEach((value) => + const output = Channel(); + (async () => { + await readOnly.forEach(value => output.push(callbackfn.call(thisArg, value)) - ) + ); - output.close() - })() + output.close(); + })(); - return output + return output; }, readOnly: () => readOnly, reduce: async (callbackfn, ...initialValue) => { - let previousValue = initialValue[0] - let previousValueDefined = initialValue.length > 0 + let previousValue = initialValue[0]; + let previousValueDefined = initialValue.length > 0; - await readOnly.forEach((currentValue) => { + await readOnly.forEach(currentValue => { if (previousValueDefined) { - previousValue = callbackfn(previousValue, currentValue) + previousValue = callbackfn(previousValue, currentValue); } else { - previousValue = currentValue - previousValueDefined = true + previousValue = currentValue; + previousValueDefined = true; } - }) + }); if (previousValueDefined) { - return previousValue + return previousValue; } else { throw new TypeError( `No values in channel and initialValue wasn't provided.` - ) + ); } }, - shift: function () { - const {order, promise} = Order(this) - shifts.push(order) - setImmediate(processOrders) - return Object.freeze(promise) + shift: function() { + const { order, promise } = Order(this); + shifts.push(order); + setImmediate(processOrders); + return Object.freeze(promise); }, slice: (start, end = Infinity) => { - const output = Channel() - - ;(async () => { + const output = Channel(); + (async () => { for (let index = 0; index < end; index++) { - const value = await readOnly.shift() + const value = await readOnly.shift(); if (value === undefined) { - break + break; } else if (index >= start) { - await output.push(value) + await output.push(value); } } - await output.close() - })() + await output.close(); + })(); - return output + return output; } - }) + }); - Object.defineProperty(readOnly, `value`, {get: () => lastValue}) - Object.freeze(readOnly) + Object.defineProperty(readOnly, `value`, { get: () => lastValue }); + Object.freeze(readOnly); const writeOnly = Object.freeze({ close: () => new Promise((resolve, reject) => { if (closed) { - reject(new Error(`Can't close an already-closed channel.`)) + reject(new Error(`Can't close an already-closed channel.`)); } else { - closed = true - processOrders() + closed = true; + processOrders(); // Give remaining orders in flight time to resolve before returning. - setImmediate(resolve) + setImmediate(resolve); } }), - push: function (value) { - const {order, promise} = Order(this) - order.value = value + push: function(value) { + const { order, promise } = Order(this); + order.value = value; if (closed) { - order.reject(new Error(`Can't push to closed channel.`)) + order.reject(new Error(`Can't push to closed channel.`)); } else if (value === undefined) { - order.reject(new TypeError( - `Can't push 'undefined' to channel, use close instead.` - )) + order.reject( + new TypeError(`Can't push 'undefined' to channel, use close instead.`) + ); } else if (arguments.length > 1) { - order.reject(new Error(`Can't push more than one value at a time.`)) + order.reject(new Error(`Can't push more than one value at a time.`)); } else { - pushes.push(order) - setImmediate(processOrders) + pushes.push(order); + setImmediate(processOrders); } - return Object.freeze(promise) + return Object.freeze(promise); }, writeOnly: () => writeOnly - }) + }); // Use Object.create because readOnly has a getter. - return Object.freeze(Object.assign(Object.create(readOnly), writeOnly)) -} + return Object.freeze(Object.assign(Object.create(readOnly), writeOnly)); +}; -Channel.from = (values) => { - const channel = Channel() - - ;(async () => { +Channel.from = values => { + const channel = Channel(); + (async () => { try { // iterable for (let value of values) { - await channel.push(value) + await channel.push(value); } - await channel.close() + await channel.close(); } catch (exception) { // Assume it's a Node.js stream.readable. - values.on('readable', async () => { + values.on("readable", async () => { while (true) { - const data = values.read() + const data = values.read(); if (data === null) { - break + break; } else { - await channel.push(data) + await channel.push(data); } } - }) + }); - values.once('end', channel.close) + values.once("end", channel.close); } - })() + })(); - return channel -} + return channel; +}; -Channel.of = (...values) => - Channel.from(values) +Channel.of = (...values) => Channel.from(values); -Channel.isChannel = (arg) => - prototype.isPrototypeOf(arg) +Channel.isChannel = arg => prototype.isPrototypeOf(arg); Channel.select = (...methodPromises) => Object.assign( new Promise((resolve, reject) => { - methodPromises.forEach(async (promise) => { + methodPromises.forEach(async promise => { promise.prethen(() => { // We've been given a heads-up that this method will complete first so // cancel the other method calls. - methodPromises.forEach((other) => { + methodPromises.forEach(other => { if (other !== promise) { - other.cancel() + other.cancel(); } - }) - }) + }); + }); try { - await promise + await promise; } catch (exception) { - reject(exception) + reject(exception); } - resolve(promise.channel) - }) + resolve(promise.channel); + }); }), { - cancel: () => - Promise.all(methodPromises.map((promise) => promise.cancel())) + cancel: () => Promise.all(methodPromises.map(promise => promise.cancel())) } - ) + ); // functional interface allowing full or partial application // @@ -347,19 +337,17 @@ Channel.select = (...methodPromises) => // const skipTen = Channel.slice(10, Infinity) // skipTen(channel) -const channel = Channel() -const methods = Object.keys(channel).concat(Object.keys(channel.readOnly())) +const channel = Channel(); +const methods = Object.keys(channel).concat(Object.keys(channel.readOnly())); -methods.forEach((method) => { +methods.forEach(method => { Channel[method] = (...args) => { - const arity = method === `slice` - ? 3 - : channel[method].length + const arity = method === `slice` ? 3 : channel[method].length; return args.length >= arity ? args[arity - 1][method](...args.slice(0, arity - 1)) - : (channel) => channel[method](...args) - } -}) + : channel => channel[method](...args); + }; +}); -module.exports = Object.freeze(Channel) +module.exports = Object.freeze(Channel); diff --git a/package.json b/package.json index 9ad5d0e..4c35dac 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "@nodeguy/assert": "0.1.1", "husky": "0.14.3", "mocha": "3.5.3", - "standard": "10.0.3" + "prettier": "1.7.4" }, "homepage": "https://github.com/NodeGuy/channel", "keywords": [ @@ -30,8 +30,7 @@ "url": "git+https://github.com/NodeGuy/channel.git" }, "scripts": { - "precommit": "standard", - "pretest": "standard", + "precommit": "prettier --write lib/**/*.js test/**/*.js", "test": "mocha --recursive" }, "standard": { diff --git a/test/go/doubleselect.js b/test/go/doubleselect.js index 30f53ad..7f2ce13 100644 --- a/test/go/doubleselect.js +++ b/test/go/doubleselect.js @@ -5,73 +5,73 @@ // Test the situation in which two cases of a select can // both end up running. See http://codereview.appspot.com/180068. -'use strict' +"use strict"; -const Channel = require('../../lib') +const Channel = require("../../lib"); -it(`doubleselect`, async function () { - this.timeout(10 * 1000) - const iterations = 100000 // number of iterations +it(`doubleselect`, async function() { + this.timeout(10 * 1000); + const iterations = 100000; // number of iterations // sender sends a counter to one of four different channels. If two cases both // end up running in the same iteration, the same value will be sent to two // different channels. const sender = async (n, c1, c2, c3, c4) => { for (let i = 0; i < n; i++) { - await Channel.select(c1.push(i), c2.push(i), c3.push(i), c4.push(i)) + await Channel.select(c1.push(i), c2.push(i), c3.push(i), c4.push(i)); } - c1.close() - c2.close() - c3.close() - c4.close() - } + c1.close(); + c2.close(); + c3.close(); + c4.close(); + }; // mux receives the values from sender and forwards them onto another channel. // It would be simpler to just have sender's four cases all be the same // channel, but this doesn't actually trigger the bug. const mux = async (output, input, done) => { - await input.forEach(async (value) => { - await output.push(value) - }) + await input.forEach(async value => { + await output.push(value); + }); - await done.push(true) - } + await done.push(true); + }; // recver gets a steam of values from the four mux's and checks for // duplicates. - const recver = (input) => { - const seen = new Map() + const recver = input => { + const seen = new Map(); - input.forEach((v) => { + input.forEach(v => { if (seen.has(v)) { - throw new Error(`got duplicate value: ${v}`) + throw new Error(`got duplicate value: ${v}`); } - seen.set(v, true) - }) - } + seen.set(v, true); + }); + }; - const c1 = Channel() - const c2 = Channel() - const c3 = Channel() - const c4 = Channel() - const done = Channel() - const cmux = Channel() - sender(iterations, c1, c2, c3, c4) - mux(cmux, c1, done) - mux(cmux, c2, done) - mux(cmux, c3, done) - mux(cmux, c4, done) + const c1 = Channel(); + const c2 = Channel(); + const c3 = Channel(); + const c4 = Channel(); + const done = Channel(); + const cmux = Channel(); + sender(iterations, c1, c2, c3, c4); + mux(cmux, c1, done); + mux(cmux, c2, done); + mux(cmux, c3, done); + mux(cmux, c4, done); // We keep the recver because it might catch more bugs in the future. // However, the result of the bug linked to at the top is that we'll // end up panicking with: "throw: bad g->status in ready". - recver(cmux) + recver(cmux); - await done.shift() - await done.shift() - await done.shift() - await done.shift() - cmux.close() -}) + await done.shift(); + await done.shift(); + await done.shift(); + await done.shift(); + cmux.close(); +}); diff --git a/test/go/fifo.js b/test/go/fifo.js index 6846962..466118d 100644 --- a/test/go/fifo.js +++ b/test/go/fifo.js @@ -4,58 +4,58 @@ // Test that unbuffered channels act as pure fifos. -'use strict' +"use strict"; -const Channel = require('../../lib') +const Channel = require("../../lib"); -it(`fifo`, function () { - const N = 10 +it(`fifo`, function() { + const N = 10; const AsynchFifo = async () => { - const ch = Channel(10) + const ch = Channel(10); for (let i = 0; i < N; i++) { - await ch.push(i) + await ch.push(i); } for (let i = 0; i < N; i++) { - if (await ch.shift() !== i) { - throw new Error(`bad receive`) + if ((await ch.shift()) !== i) { + throw new Error(`bad receive`); } } - } + }; const Chain = async (ch, val, input, output) => { - await input.shift() + await input.shift(); - if (await ch.shift() !== val) { - throw new Error(val) + if ((await ch.shift()) !== val) { + throw new Error(val); } - await output.push(1) - } + await output.push(1); + }; // thread together a daisy chain to read the elements in sequence const SynchFifo = async () => { - const ch = Channel() - let input = Channel() - let start = input + const ch = Channel(); + let input = Channel(); + let start = input; for (let i = 0; i < N; i++) { - const output = Channel() - Chain(ch, i, input, output) - input = output + const output = Channel(); + Chain(ch, i, input, output); + input = output; } - await start.push(0) + await start.push(0); for (let i = 0; i < N; i++) { - await ch.push(i) + await ch.push(i); } - await input.shift() - } + await input.shift(); + }; - AsynchFifo() - SynchFifo() -}) + AsynchFifo(); + SynchFifo(); +}); diff --git a/test/go/goroutines.js b/test/go/goroutines.js index c2c7d7c..570545d 100644 --- a/test/go/goroutines.js +++ b/test/go/goroutines.js @@ -5,29 +5,29 @@ // Torture test for goroutines. // Make a lot of goroutines, threaded together, and tear them down cleanly. -'use strict' +"use strict"; -const Channel = require('../../lib') +const Channel = require("../../lib"); -it(`goroutines`, async function () { +it(`goroutines`, async function() { const f = async (left, right) => { - await left.push(await right.shift()) - } + await left.push(await right.shift()); + }; - const n = 10000 - const leftmost = Channel() - let right = leftmost - let left = leftmost + const n = 10000; + const leftmost = Channel(); + let right = leftmost; + let left = leftmost; for (let i = 0; i < n; i++) { - right = Channel() - f(left, right) - left = right + right = Channel(); + f(left, right); + left = right; } - ;(async (c) => { - await c.push(1) - })(right) + (async c => { + await c.push(1); + })(right); - await leftmost.shift() -}) + await leftmost.shift(); +}); diff --git a/test/go/perm.js b/test/go/perm.js index 8344478..76f196c 100644 --- a/test/go/perm.js +++ b/test/go/perm.js @@ -7,72 +7,72 @@ // Test various correct and incorrect permutations of send-only, // receive-only, and bidirectional channels. -'use strict' +"use strict"; -const assert = require(`@nodeguy/assert`) -const Channel = require('../../lib') +const assert = require(`@nodeguy/assert`); +const Channel = require("../../lib"); -it(`perm`, function () { - const c = Channel() - const cr = Channel().readOnly() - const cs = Channel().writeOnly() +it(`perm`, function() { + const c = Channel(); + const cr = Channel().readOnly(); + const cs = Channel().writeOnly(); - const n = 0 + const n = 0; assert.throws(() => { - Channel.shift(n) // ERROR "receive from non-chan" - }) + Channel.shift(n); // ERROR "receive from non-chan" + }); assert.throws(() => { - Channel.push(2, n) // ERROR "send to non-chan" - }) + Channel.push(2, n); // ERROR "send to non-chan" + }); - c.push(0) // ok - c.shift() // ok + c.push(0); // ok + c.shift(); // ok assert.throws(() => { - cr.push(0) // ERROR "send" - }) + cr.push(0); // ERROR "send" + }); - cr.shift() // ok + cr.shift(); // ok - cs.push(0) // ok + cs.push(0); // ok assert.throws(() => { - cs.shift() // ERROR "receive" - }) + cs.shift(); // ERROR "receive" + }); Channel.select( c.push(0), // ok - c.shift() // ok - ) + c.shift() // ok + ); assert.throws(() => { Channel.select( cr.push(0) // ERROR "send" - ) - }) + ); + }); - Channel.select(cr.shift()) // ok + Channel.select(cr.shift()); // ok - Channel.select(cs.push(0)) // ok + Channel.select(cs.push(0)); // ok assert.throws(() => { - Channel.select(cs.shift()) // ERROR "receive" - }) + Channel.select(cs.shift()); // ERROR "receive" + }); assert.throws(() => { - cs.forEach(() => {}) // ERROR "receive" - }) + cs.forEach(() => {}); // ERROR "receive" + }); - c.close() - cs.close() + c.close(); + cs.close(); assert.throws(() => { - cr.close() // ERROR "receive" - }) + cr.close(); // ERROR "receive" + }); assert.throws(() => { - Channel.close(n) // ERROR "invalid operation.*non-chan type" - }) -}) + Channel.close(n); // ERROR "invalid operation.*non-chan type" + }); +}); diff --git a/test/go/select.js b/test/go/select.js index 52a3775..1d9a9d5 100644 --- a/test/go/select.js +++ b/test/go/select.js @@ -4,70 +4,73 @@ // Test simple select. -'use strict' +"use strict"; -const Channel = require('../../lib') +const Channel = require("../../lib"); -it(`select`, async function () { - const closed = Channel() - closed.close() - let counter = 0 - let shift = 0 +it(`select`, async function() { + const closed = Channel(); + closed.close(); + let counter = 0; + let shift = 0; const GetValue = () => { - counter++ - return 1 << shift - } + counter++; + return 1 << shift; + }; const Send = async (a, b) => { - let done = false - let i = 0 + let done = false; + let i = 0; do { - switch (await Channel.select(a.push(GetValue()), b.push(GetValue()), - closed.shift())) { + switch (await Channel.select( + a.push(GetValue()), + b.push(GetValue()), + closed.shift() + )) { case a: - i++ - a = Channel() - break + i++; + a = Channel(); + break; case b: - i++ - b = Channel() - break + i++; + b = Channel(); + break; default: - done = true + done = true; } - shift++ - } while (!done) + shift++; + } while (!done); - return i - } + return i; + }; - let a = Channel(1) - let b = Channel(1) - let v = await Send(a, b) + let a = Channel(1); + let b = Channel(1); + let v = await Send(a, b); if (v !== 2) { - throw new Error(`Send returned ${v} !== 2`) + throw new Error(`Send returned ${v} !== 2`); } - const av = await a.shift() - const bv = await b.shift() + const av = await a.shift(); + const bv = await b.shift(); if ((av | bv) !== 3) { - throw new Error(`bad values ${av} ${bv}`) + throw new Error(`bad values ${av} ${bv}`); } - v = await Send(a, Channel()) + v = await Send(a, Channel()); if (v !== 1) { - throw new Error(`Send returned ${v} !== 1`) + throw new Error(`Send returned ${v} !== 1`); } if (counter !== 10) { - throw new Error(`counter is ${counter} !== 10`) + throw new Error(`counter is ${counter} !== 10`); } -}) +}); diff --git a/test/index.js b/test/index.js index 09e9450..2afb25f 100644 --- a/test/index.js +++ b/test/index.js @@ -1,415 +1,393 @@ -'use strict' +"use strict"; -const assert = require('@nodeguy/assert') -const Channel = require('../lib') -const stream = require('stream') +const assert = require("@nodeguy/assert"); +const Channel = require("../lib"); +const stream = require("stream"); const assertRejects = async (callback, reason) => { try { - await callback() + await callback(); } catch (exception) { if (reason) { - assert.deepEqual(exception, reason) + assert.deepEqual(exception, reason); } - return + return; } - assert.fail(null, reason, `Missing expected rejection.`) -} + assert.fail(null, reason, `Missing expected rejection.`); +}; -const toArray = async (channel) => { - const array = [] +const toArray = async channel => { + const array = []; - await channel.forEach((item) => { - array.push(item) - }) + await channel.forEach(item => { + array.push(item); + }); - return array -} + return array; +}; -describe(`Channel`, function () { - it(`allows the use of new`, function () { - return new Channel() - }) +describe(`Channel`, function() { + it(`allows the use of new`, function() { + return new Channel(); + }); - it(`is frozen`, function () { + it(`is frozen`, function() { assert.throws(() => { - Channel.frozen = false - }) - }) + Channel.frozen = false; + }); + }); - it(`creates a frozen object`, function () { + it(`creates a frozen object`, function() { assert.throws(() => { - Channel().frozen = false - }) - }) + Channel().frozen = false; + }); + }); - it(`creates a buffered channel`, async function () { - const channel = Channel(2) + it(`creates a buffered channel`, async function() { + const channel = Channel(2); + (async () => { + assert.equal(await channel.shift(), 0); + })(); - ;(async () => { - assert.equal(await channel.shift(), 0) - })() + await channel.push(0); + await channel.push(1); + await channel.push(2); + }); - await channel.push(0) - await channel.push(1) - await channel.push(2) - }) + describe(`from`, function() { + it(`iterable`, async function() { + assert.deepEqual(await toArray(Channel.from([0, 1, 2])), [0, 1, 2]); + }); - describe(`from`, function () { - it(`iterable`, async function () { - assert.deepEqual(await toArray(Channel.from([0, 1, 2])), [0, 1, 2]) - }) + it(`Node.js's stream.readOnly`, async function() { + const readOnly = stream.PassThrough({ objectMode: true }); + readOnly.write(0); + readOnly.write(1); + readOnly.end(2); + assert.deepEqual(await toArray(Channel.from(readOnly)), [0, 1, 2]); + }); + }); - it(`Node.js's stream.readOnly`, async function () { - const readOnly = stream.PassThrough({objectMode: true}) - readOnly.write(0) - readOnly.write(1) - readOnly.end(2) - assert.deepEqual(await toArray(Channel.from(readOnly)), [0, 1, 2]) - }) - }) + it(`isChannel`, function() { + assert(Channel.isChannel(Channel.of(0, 1, 2))); + assert(!Channel.isChannel(Array.of(0, 1, 2))); + }); - it(`isChannel`, function () { - assert(Channel.isChannel(Channel.of(0, 1, 2))) - assert(!Channel.isChannel(Array.of(0, 1, 2))) - }) + it(`of`, async function() { + assert.deepEqual(await toArray(Channel.of(0, 1, 2)), [0, 1, 2]); + }); - it(`of`, async function () { - assert.deepEqual(await toArray(Channel.of(0, 1, 2)), [0, 1, 2]) - }) + describe(`select`, function() { + it(`miscellaneous`, async function() { + const a = Channel(); + const b = Channel(); + (async () => { + await b.push(0); + await a.push(1); + await a.shift(); + })(); - describe(`select`, function () { - it(`miscellaneous`, async function () { - const a = Channel() - const b = Channel() + assert.equal(await Channel.select(a.shift(), b.shift()), b); + assert.equal(b.value, 0); + assert.equal(await a.shift(), 1); + assert.equal(await Channel.select(a.push(0), b.shift()), a); + }); + }); - ;(async () => { - await b.push(0) - await a.push(1) - await a.shift() - })() - - assert.equal(await Channel.select(a.shift(), b.shift()), b) - assert.equal(b.value, 0) - assert.equal(await a.shift(), 1) - assert.equal(await Channel.select(a.push(0), b.shift()), a) - }) - }) - - it(`allows for non-blocking selects`, async function () { - const a = Channel() - const b = Channel() - const nonBlocking = Channel() - nonBlocking.close() + it(`allows for non-blocking selects`, async function() { + const a = Channel(); + const b = Channel(); + const nonBlocking = Channel(); + nonBlocking.close(); switch (await Channel.select(a.shift(), b.push(0), nonBlocking.shift())) { case a: - assert(false) - break + assert(false); + break; case b: - assert(false) - break + assert(false); + break; default: - assert(true) - break + assert(true); + break; } - }) + }); - it(`cancel`, async function () { - const channel = Channel() - Channel.select(channel.push(`cancelled`)).cancel() - const closed = Channel.of() - assert.equal(await Channel.select(channel.shift(), closed.shift()), closed) - }) -}) + it(`cancel`, async function() { + const channel = Channel(); + Channel.select(channel.push(`cancelled`)).cancel(); + const closed = Channel.of(); + assert.equal(await Channel.select(channel.shift(), closed.shift()), closed); + }); +}); -describe(`functional interface`, async function () { - describe(`map`, function () { - it(`full application`, async function () { +describe(`functional interface`, async function() { + describe(`map`, function() { + it(`full application`, async function() { assert.deepEqual( - await toArray(Channel.map( - (value) => value.toUpperCase(), - Channel.of(`a`, `b`, `c`) - )), - [`A`, `B`, `C`] - ) - }) - - it(`partial application`, async function () { - assert.deepEqual( - await toArray(Channel.map((value) => - value.toUpperCase())(Channel.of(`a`, `b`, `c`)) + await toArray( + Channel.map(value => value.toUpperCase(), Channel.of(`a`, `b`, `c`)) ), [`A`, `B`, `C`] - ) - }) - }) + ); + }); - describe(`slice`, function () { - it(`full application`, async function () { + it(`partial application`, async function() { + assert.deepEqual( + await toArray( + Channel.map(value => value.toUpperCase())(Channel.of(`a`, `b`, `c`)) + ), + [`A`, `B`, `C`] + ); + }); + }); + + describe(`slice`, function() { + it(`full application`, async function() { assert.deepEqual( await toArray(Channel.slice(1, 4, Channel.of(0, 1, 2, 3, 4))), [1, 2, 3] - ) - }) + ); + }); - it(`partial application`, async function () { + it(`partial application`, async function() { assert.deepEqual( await toArray(Channel.slice(1, 4)(Channel.of(0, 1, 2, 3, 4))), [1, 2, 3] - ) - }) - }) -}) + ); + }); + }); +}); -describe(`Channel object`, function () { - describe(`close`, function () { - it(`can't close an already closed channel`, function () { - const channel = Channel() - channel.close() +describe(`Channel object`, function() { + describe(`close`, function() { + it(`can't close an already closed channel`, function() { + const channel = Channel(); + channel.close(); return assertRejects(async () => { - await channel.close() - }, new Error(`Can't close an already-closed channel.`)) - }) + await channel.close(); + }, new Error(`Can't close an already-closed channel.`)); + }); - it(`can't push to a closed channel`, async function () { - const channel = Channel() - channel.close() + it(`can't push to a closed channel`, async function() { + const channel = Channel(); + channel.close(); return assertRejects(async () => { - await channel.push(0) - }, new Error(`Can't push to closed channel.`)) - }) + await channel.push(0); + }, new Error(`Can't push to closed channel.`)); + }); - it(`returns 'undefined' immediately from shift`, async function () { - const channel = Channel() - channel.close() - assert.strictEqual(await channel.shift(), undefined) - }) - }) + it(`returns 'undefined' immediately from shift`, async function() { + const channel = Channel(); + channel.close(); + assert.strictEqual(await channel.shift(), undefined); + }); + }); - it(`every`, async function () { - const even = (number) => number % 2 === 0 - assert(!await Channel.of(0, 1, 2).every(even)) - assert(await Channel.of(0, 2, 4).every(even)) - }) + it(`every`, async function() { + const even = number => number % 2 === 0; + assert(!await Channel.of(0, 1, 2).every(even)); + assert(await Channel.of(0, 2, 4).every(even)); + }); - it(`filter`, async function () { + it(`filter`, async function() { assert.deepEqual( - await toArray(Channel.of(0, 1, 2, 3, 4, 5) - .filter((value) => value % 2 !== 0) + await toArray( + Channel.of(0, 1, 2, 3, 4, 5).filter(value => value % 2 !== 0) ), [1, 3, 5] - ) - }) + ); + }); - it(`forEach`, async function () { - const output = Channel() + it(`forEach`, async function() { + const output = Channel(); + (async () => { + await Channel.of(0, 1, 2).forEach(output.push); + output.close(); + })(); - ;(async () => { - await Channel.of(0, 1, 2).forEach(output.push) - output.close() - })() + assert.deepEqual(await toArray(output), [0, 1, 2]); + }); - assert.deepEqual(await toArray(output), [0, 1, 2]) - }) + it(`join`, async function() { + assert.equal(await Channel.of(`a`, `b`, `c`).join(), `a,b,c`); + }); - it(`join`, async function () { - assert.equal(await Channel.of(`a`, `b`, `c`).join(), `a,b,c`) - }) - - it(`map`, async function () { + it(`map`, async function() { assert.deepEqual( - await toArray(Channel.of(`a`, `b`, `c`) - .map((value) => value.toUpperCase()) + await toArray( + Channel.of(`a`, `b`, `c`).map(value => value.toUpperCase()) ), [`A`, `B`, `C`] - ) - }) + ); + }); - describe(`push`, function () { - it(`with shift`, async function () { - const channel = Channel() + describe(`push`, function() { + it(`with shift`, async function() { + const channel = Channel(); + (async () => { + await channel.push(0); + })(); - ;(async () => { - await channel.push(0) - })() + assert.equal(await channel.shift(), 0); + }); - assert.equal(await channel.shift(), 0) - }) - - describe(`undefined`, function () { - it(`outside select`, function () { - const channel = Channel() - - return assertRejects( - async () => { - await channel.push(undefined) - }, - new TypeError(`Can't push 'undefined' to channel, use close instead.`) - ) - }) - - it(`inside select`, function () { - const channel = Channel() + describe(`undefined`, function() { + it(`outside select`, function() { + const channel = Channel(); return assertRejects(async () => { - await Channel.select(channel.push(undefined)) - }, new TypeError( - `Can't push 'undefined' to channel, use close instead.` - )) - }) - }) + await channel.push(undefined); + }, new TypeError(`Can't push 'undefined' to channel, use close instead.`)); + }); - it(`disallows multiple values`, function () { - const channel = Channel() + it(`inside select`, function() { + const channel = Channel(); + + return assertRejects(async () => { + await Channel.select(channel.push(undefined)); + }, new TypeError(`Can't push 'undefined' to channel, use close instead.`)); + }); + }); + + it(`disallows multiple values`, function() { + const channel = Channel(); return assertRejects(async () => { - await channel.push(0, 1, 2) - }, new Error(`Can't push more than one value at a time.`)) - }) + await channel.push(0, 1, 2); + }, new Error(`Can't push more than one value at a time.`)); + }); - it(`returns a frozen promise`, function () { + it(`returns a frozen promise`, function() { assert.throws(() => { - Channel().push(0).frozen = false - }) - }) - }) + Channel().push(0).frozen = false; + }); + }); + }); - it(`readOnly`, async function () { - const channel = Channel() - const readOnly = channel.readOnly() + it(`readOnly`, async function() { + const channel = Channel(); + const readOnly = channel.readOnly(); assert.throws(() => { - readOnly.close() - }) + readOnly.close(); + }); assert.throws(() => { - readOnly.push(0) - }) + readOnly.push(0); + }); assert.throws(() => { - readOnly.writeOnly() - }) + readOnly.writeOnly(); + }); + (async () => { + await channel.push(1); + })(); - ;(async () => { - await channel.push(1) - })() - - assert.equal(readOnly.readOnly(), readOnly) - assert.equal(await readOnly.shift(), 1) - assert.equal(readOnly.value, 1) + assert.equal(readOnly.readOnly(), readOnly); + assert.equal(await readOnly.shift(), 1); + assert.equal(readOnly.value, 1); assert.throws(() => { - readOnly.frozen = false - }) - }) + readOnly.frozen = false; + }); + }); - describe(`reduce`, function () { - it(`callbackfn only`, async function () { - assert.equal(await Channel.of(0, 1, 2) - .reduce(Math.max), - 2 - ) - }) + describe(`reduce`, function() { + it(`callbackfn only`, async function() { + assert.equal(await Channel.of(0, 1, 2).reduce(Math.max), 2); + }); - it(`initialValue`, async function () { - assert.equal(await Channel.of(0, 1, 2) - .reduce(Math.max, 10), - 10 - ) - }) + it(`initialValue`, async function() { + assert.equal(await Channel.of(0, 1, 2).reduce(Math.max, 10), 10); + }); - it(`no values without initialValue`, function () { - return assertRejects( - async () => { - await Channel.of().reduce(Math.max) - }, - new TypeError(`No values in channel and initialValue wasn't provided.`) - ) - }) - }) + it(`no values without initialValue`, function() { + return assertRejects(async () => { + await Channel.of().reduce(Math.max); + }, new TypeError(`No values in channel and initialValue wasn't provided.`)); + }); + }); - describe(`shift`, function () { - it(`with push`, async function () { - const channel = Channel() + describe(`shift`, function() { + it(`with push`, async function() { + const channel = Channel(); + (async () => { + await channel.push(0); + })(); - ;(async () => { - await channel.push(0) - })() + assert.equal(await channel.shift(), 0); + }); - assert.equal(await channel.shift(), 0) - }) - - it(`returns a frozen promise`, function () { + it(`returns a frozen promise`, function() { assert.throws(() => { - Channel().shift().frozen = false - }) - }) - }) + Channel().shift().frozen = false; + }); + }); + }); - describe(`slice`, function () { - it(`start`, async function () { - assert.deepEqual(await toArray(Channel.of(0, 1, 2).slice(1)), [1, 2]) - }) + describe(`slice`, function() { + it(`start`, async function() { + assert.deepEqual(await toArray(Channel.of(0, 1, 2).slice(1)), [1, 2]); + }); - it(`end`, async function () { - assert.deepEqual( - await toArray(Channel.of(0, 1, 2, 3, 4).slice(1, 4)), - [1, 2, 3] - ) - }) - }) + it(`end`, async function() { + assert.deepEqual(await toArray(Channel.of(0, 1, 2, 3, 4).slice(1, 4)), [ + 1, + 2, + 3 + ]); + }); + }); - it(`value`, async function () { - const channel = Channel() + it(`value`, async function() { + const channel = Channel(); + (async () => { + await channel.push(0); + })(); - ;(async () => { - await channel.push(0) - })() - - await channel.shift() - assert.equal(channel.value, 0) + await channel.shift(); + assert.equal(channel.value, 0); assert.throws(() => { - channel.value = 1 - }) + channel.value = 1; + }); - channel.close() - await channel.shift() - assert.equal(channel.value, undefined) - }) + channel.close(); + await channel.shift(); + assert.equal(channel.value, undefined); + }); - describe(`writeOnly`, function () { - it(`provides only write methods`, async function () { - const channel = Channel() - const writeOnly = channel.writeOnly() + describe(`writeOnly`, function() { + it(`provides only write methods`, async function() { + const channel = Channel(); + const writeOnly = channel.writeOnly(); assert.throws(() => { - writeOnly.readOnly() - }) + writeOnly.readOnly(); + }); assert.throws(() => { - writeOnly.shift() - }) + writeOnly.shift(); + }); - assert.equal(writeOnly.value, undefined) + assert.equal(writeOnly.value, undefined); + (async () => { + await channel.shift(); + })(); - ;(async () => { - await channel.shift() - })() - - await writeOnly.push(0) - writeOnly.close() + await writeOnly.push(0); + writeOnly.close(); assert.throws(() => { - writeOnly.frozen = false - }) - }) - }) -}) + writeOnly.frozen = false; + }); + }); + }); +});