reduce: Throw TypeError if the channel contains no values and initialValue is not provided.

This commit is contained in:
David Braun 2017-10-13 21:32:27 -04:00
parent 7d0d203c2e
commit ecc01b686e
No known key found for this signature in database
GPG key ID: 5694EEC4D129BDCF
3 changed files with 77 additions and 54 deletions

View file

@ -257,6 +257,21 @@ value has been shifted out or placed in the buffer.
reserved value used to indicate a closed channel.
#### 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
Returns a promise that resolves when an value is received from the channel.

View file

@ -182,14 +182,20 @@ const Channel = function (bufferLength = 0) {
await readOnly.forEach((currentValue) => {
if (previousValueDefined) {
previousValue = callbackfn(previousValue, currentValue, 0, readOnly)
previousValue = callbackfn(previousValue, currentValue)
} else {
previousValue = currentValue
previousValueDefined = true
}
})
return previousValue
if (previousValueDefined) {
return previousValue
} else {
throw new TypeError(
`No values in channel and initialValue wasn't provided.`
)
}
},
shift: function () {

View file

@ -4,6 +4,20 @@ const assert = require('@nodeguy/assert')
const Channel = require('../lib')
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 array = []
@ -147,35 +161,22 @@ describe(`Channel`, function () {
describe(`Channel object`, 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()
channel.close()
channel.close()
.then(() => {
done(new Error())
})
.catch((reason) => {
assert.deepEqual(
reason,
new Error(`Can't close an already-closed channel.`)
)
done()
})
return assertRejects(async () => {
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()
return (async () => {
return assertRejects(async () => {
await channel.push(0)
})().then(() => {
assert(false)
}).catch((reason) => {
assert.deepEqual(reason, new Error(`Can't push to closed channel.`))
})
}, new Error(`Can't push to closed channel.`))
})
it(`returns 'undefined' immediately from shift`, async function () {
@ -239,43 +240,31 @@ describe(`Channel object`, function () {
it(`outside select`, function () {
const channel = Channel()
return (async () => {
await channel.push(undefined)
})().then(() => {
assert(false)
}).catch((reason) => {
assert.deepEqual(reason, 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()
return (async () => {
return assertRejects(async () => {
await Channel.select(channel.push(undefined))
})().then(() => {
assert(false)
}).catch((reason) => {
assert.deepEqual(reason, new TypeError(
`Can't push 'undefined' to channel, use close instead.`
))
})
}, new TypeError(
`Can't push 'undefined' to channel, use close instead.`
))
})
})
it(`disallows multiple values`, function () {
const channel = Channel()
return (async () => {
return assertRejects(async () => {
await channel.push(0, 1, 2)
})().then(() => {
assert(false)
}).catch((reason) => {
assert.deepEqual(reason, new Error(
`Can't push more than one value at a time.`))
})
}, new Error(`Can't push more than one value at a time.`))
})
it(`returns a frozen promise`, function () {
@ -314,16 +303,29 @@ describe(`Channel object`, function () {
})
})
it(`reduce`, async function () {
assert.equal(await Channel.of(0, 1, 2)
.reduce((previous, current) => previous + current),
3
)
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((previous, current) => previous + current, 10),
13
)
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.`)
)
})
})
describe(`shift`, function () {