reduce: Throw TypeError if the channel contains no values and initialValue
is not provided.
This commit is contained in:
parent
7d0d203c2e
commit
ecc01b686e
3 changed files with 77 additions and 54 deletions
15
README.md
15
README.md
|
@ -257,6 +257,21 @@ value has been shifted out or placed in the buffer.
|
||||||
reserved value used to indicate a closed channel.
|
reserved value used to indicate a closed channel.
|
||||||
|
|
||||||
#### reduce(callbackfn[, initialValue])
|
#### reduce(callbackfn[, initialValue])
|
||||||
|
|
||||||
|
`callbackfn` should be a function that takes two arguments (unlike `Array`'s
|
||||||
|
version which takes four). `reduce` calls the callback, as a function, once for
|
||||||
|
each value after the first value present in the channel.
|
||||||
|
|
||||||
|
`callbackfn` is called with two arguments: the `previousValue` (value from the
|
||||||
|
previous call to `callbackfn`) and the `currentValue`. The first time that
|
||||||
|
callback is called, the `previousValue` and `currentValue` can be one of two
|
||||||
|
values. If an `initialValue` was provided in the call to `reduce`, then
|
||||||
|
`previousValue` will be equal to `initialValue` and `currentValue` will be equal
|
||||||
|
to the first value in the channel. If no `initialValue` was provided, then
|
||||||
|
`previousValue` will be equal to the first value in the channel and
|
||||||
|
`currentValue` will be equal to the second. It is a `TypeError` if the channel
|
||||||
|
contains no values and `initialValue` is not provided.
|
||||||
|
|
||||||
#### shift() -> async
|
#### shift() -> async
|
||||||
|
|
||||||
Returns a promise that resolves when an value is received from the channel.
|
Returns a promise that resolves when an value is received from the channel.
|
||||||
|
|
|
@ -182,14 +182,20 @@ const Channel = function (bufferLength = 0) {
|
||||||
|
|
||||||
await readOnly.forEach((currentValue) => {
|
await readOnly.forEach((currentValue) => {
|
||||||
if (previousValueDefined) {
|
if (previousValueDefined) {
|
||||||
previousValue = callbackfn(previousValue, currentValue, 0, readOnly)
|
previousValue = callbackfn(previousValue, currentValue)
|
||||||
} else {
|
} else {
|
||||||
previousValue = currentValue
|
previousValue = currentValue
|
||||||
previousValueDefined = true
|
previousValueDefined = true
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (previousValueDefined) {
|
||||||
return previousValue
|
return previousValue
|
||||||
|
} else {
|
||||||
|
throw new TypeError(
|
||||||
|
`No values in channel and initialValue wasn't provided.`
|
||||||
|
)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
shift: function () {
|
shift: function () {
|
||||||
|
|
|
@ -4,6 +4,20 @@ const assert = require('@nodeguy/assert')
|
||||||
const Channel = require('../lib')
|
const Channel = require('../lib')
|
||||||
const stream = require('stream')
|
const stream = require('stream')
|
||||||
|
|
||||||
|
const assertRejects = async (callback, reason) => {
|
||||||
|
try {
|
||||||
|
await callback()
|
||||||
|
} catch (exception) {
|
||||||
|
if (reason) {
|
||||||
|
assert.deepEqual(exception, reason)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
assert.fail(null, reason, `Missing expected rejection.`)
|
||||||
|
}
|
||||||
|
|
||||||
const toArray = async (channel) => {
|
const toArray = async (channel) => {
|
||||||
const array = []
|
const array = []
|
||||||
|
|
||||||
|
@ -147,35 +161,22 @@ describe(`Channel`, function () {
|
||||||
|
|
||||||
describe(`Channel object`, function () {
|
describe(`Channel object`, function () {
|
||||||
describe(`close`, function () {
|
describe(`close`, function () {
|
||||||
it(`can't close an already closed channel`, function (done) {
|
it(`can't close an already closed channel`, function () {
|
||||||
const channel = Channel()
|
const channel = Channel()
|
||||||
channel.close()
|
channel.close()
|
||||||
|
|
||||||
channel.close()
|
return assertRejects(async () => {
|
||||||
.then(() => {
|
await channel.close()
|
||||||
done(new Error())
|
}, new Error(`Can't close an already-closed channel.`))
|
||||||
})
|
|
||||||
.catch((reason) => {
|
|
||||||
assert.deepEqual(
|
|
||||||
reason,
|
|
||||||
new Error(`Can't close an already-closed channel.`)
|
|
||||||
)
|
|
||||||
|
|
||||||
done()
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it(`can't push to a closed channel`, async function () {
|
it(`can't push to a closed channel`, async function () {
|
||||||
const channel = Channel()
|
const channel = Channel()
|
||||||
channel.close()
|
channel.close()
|
||||||
|
|
||||||
return (async () => {
|
return assertRejects(async () => {
|
||||||
await channel.push(0)
|
await channel.push(0)
|
||||||
})().then(() => {
|
}, new Error(`Can't push to closed channel.`))
|
||||||
assert(false)
|
|
||||||
}).catch((reason) => {
|
|
||||||
assert.deepEqual(reason, new Error(`Can't push to closed channel.`))
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it(`returns 'undefined' immediately from shift`, async function () {
|
it(`returns 'undefined' immediately from shift`, async function () {
|
||||||
|
@ -239,43 +240,31 @@ describe(`Channel object`, function () {
|
||||||
it(`outside select`, function () {
|
it(`outside select`, function () {
|
||||||
const channel = Channel()
|
const channel = Channel()
|
||||||
|
|
||||||
return (async () => {
|
return assertRejects(
|
||||||
|
async () => {
|
||||||
await channel.push(undefined)
|
await channel.push(undefined)
|
||||||
})().then(() => {
|
},
|
||||||
assert(false)
|
new TypeError(`Can't push 'undefined' to channel, use close instead.`)
|
||||||
}).catch((reason) => {
|
)
|
||||||
assert.deepEqual(reason, new TypeError(
|
|
||||||
`Can't push 'undefined' to channel, use close instead.`
|
|
||||||
))
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it(`inside select`, function () {
|
it(`inside select`, function () {
|
||||||
const channel = Channel()
|
const channel = Channel()
|
||||||
|
|
||||||
return (async () => {
|
return assertRejects(async () => {
|
||||||
await Channel.select(channel.push(undefined))
|
await Channel.select(channel.push(undefined))
|
||||||
})().then(() => {
|
}, new TypeError(
|
||||||
assert(false)
|
|
||||||
}).catch((reason) => {
|
|
||||||
assert.deepEqual(reason, new TypeError(
|
|
||||||
`Can't push 'undefined' to channel, use close instead.`
|
`Can't push 'undefined' to channel, use close instead.`
|
||||||
))
|
))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
|
||||||
|
|
||||||
it(`disallows multiple values`, function () {
|
it(`disallows multiple values`, function () {
|
||||||
const channel = Channel()
|
const channel = Channel()
|
||||||
|
|
||||||
return (async () => {
|
return assertRejects(async () => {
|
||||||
await channel.push(0, 1, 2)
|
await channel.push(0, 1, 2)
|
||||||
})().then(() => {
|
}, new Error(`Can't push more than one value at a time.`))
|
||||||
assert(false)
|
|
||||||
}).catch((reason) => {
|
|
||||||
assert.deepEqual(reason, new Error(
|
|
||||||
`Can't push more than one value at a time.`))
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
it(`returns a frozen promise`, function () {
|
it(`returns a frozen promise`, function () {
|
||||||
|
@ -314,18 +303,31 @@ describe(`Channel object`, function () {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it(`reduce`, async function () {
|
describe(`reduce`, function () {
|
||||||
|
it(`callbackfn only`, async function () {
|
||||||
assert.equal(await Channel.of(0, 1, 2)
|
assert.equal(await Channel.of(0, 1, 2)
|
||||||
.reduce((previous, current) => previous + current),
|
.reduce(Math.max),
|
||||||
3
|
2
|
||||||
)
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
it(`initialValue`, async function () {
|
||||||
assert.equal(await Channel.of(0, 1, 2)
|
assert.equal(await Channel.of(0, 1, 2)
|
||||||
.reduce((previous, current) => previous + current, 10),
|
.reduce(Math.max, 10),
|
||||||
13
|
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.`)
|
||||||
|
)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
describe(`shift`, function () {
|
describe(`shift`, function () {
|
||||||
it(`with push`, async function () {
|
it(`with push`, async function () {
|
||||||
const channel = Channel()
|
const channel = Channel()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue