Replace JavaScript Standard Style with Prettier.
This commit is contained in:
parent
b9638f2113
commit
d7355e17e1
9 changed files with 647 additions and 675 deletions
90
doc/API.md
90
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);
|
||||
```
|
||||
|
|
314
lib/index.js
314
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 = []
|
||||
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)
|
||||
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
|
||||
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);
|
||||
|
|
|
@ -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": {
|
||||
|
|
|
@ -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
|
||||
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();
|
||||
});
|
||||
|
|
|
@ -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
|
||||
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();
|
||||
});
|
||||
|
|
|
@ -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() {
|
||||
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();
|
||||
});
|
||||
|
|
|
@ -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()
|
||||
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
|
||||
)
|
||||
);
|
||||
|
||||
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"
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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
|
||||
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`);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
|
474
test/index.js
474
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()
|
||||
})
|
||||
return new Channel();
|
||||
});
|
||||
|
||||
it(`is frozen`, function() {
|
||||
assert.throws(() => {
|
||||
Channel.frozen = false
|
||||
})
|
||||
})
|
||||
Channel.frozen = false;
|
||||
});
|
||||
});
|
||||
|
||||
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)
|
||||
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])
|
||||
})
|
||||
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])
|
||||
})
|
||||
})
|
||||
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)))
|
||||
})
|
||||
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])
|
||||
})
|
||||
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()
|
||||
const a = Channel();
|
||||
const b = Channel();
|
||||
(async () => {
|
||||
await b.push(0);
|
||||
await a.push(1);
|
||||
await a.shift();
|
||||
})();
|
||||
|
||||
;(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)
|
||||
})
|
||||
})
|
||||
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()
|
||||
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)
|
||||
})
|
||||
})
|
||||
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() {
|
||||
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`]
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
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() {
|
||||
assert.deepEqual(
|
||||
await toArray(Channel.slice(1, 4, Channel.of(0, 1, 2, 3, 4))),
|
||||
[1, 2, 3]
|
||||
)
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
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()
|
||||
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()
|
||||
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)
|
||||
})
|
||||
})
|
||||
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))
|
||||
})
|
||||
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() {
|
||||
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()
|
||||
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`)
|
||||
})
|
||||
assert.equal(await Channel.of(`a`, `b`, `c`).join(), `a,b,c`);
|
||||
});
|
||||
|
||||
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()
|
||||
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()
|
||||
const channel = Channel();
|
||||
|
||||
return assertRejects(
|
||||
async () => {
|
||||
await channel.push(undefined)
|
||||
},
|
||||
new TypeError(`Can't push 'undefined' to channel, use close instead.`)
|
||||
)
|
||||
})
|
||||
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()
|
||||
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.select(channel.push(undefined));
|
||||
}, new TypeError(`Can't push 'undefined' to channel, use close instead.`));
|
||||
});
|
||||
});
|
||||
|
||||
it(`disallows multiple values`, function() {
|
||||
const channel = Channel()
|
||||
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() {
|
||||
assert.throws(() => {
|
||||
Channel().push(0).frozen = false
|
||||
})
|
||||
})
|
||||
})
|
||||
Channel().push(0).frozen = false;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it(`readOnly`, async function() {
|
||||
const channel = Channel()
|
||||
const readOnly = channel.readOnly()
|
||||
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
|
||||
)
|
||||
})
|
||||
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
|
||||
)
|
||||
})
|
||||
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.`)
|
||||
)
|
||||
})
|
||||
})
|
||||
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()
|
||||
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() {
|
||||
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])
|
||||
})
|
||||
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]
|
||||
)
|
||||
})
|
||||
})
|
||||
assert.deepEqual(await toArray(Channel.of(0, 1, 2, 3, 4).slice(1, 4)), [
|
||||
1,
|
||||
2,
|
||||
3
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
it(`value`, async function() {
|
||||
const channel = Channel()
|
||||
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()
|
||||
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;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue