asynchronously
Advanced tools
192
index.js
'use strict'; | ||
function delayed(fn, ms=10) { | ||
async function delayed(fn, ms = 10) { | ||
await wait(ms); | ||
return await fn(); | ||
} | ||
async function wait(ms = 10) { | ||
const timer = accept => { | ||
const t = setTimeout(accept, ms); | ||
if (t.unref) return t.unref(); | ||
return t; | ||
const time = setTimeout(accept, ms); | ||
if (typeof time.unref === 'function') time.unref(); | ||
}; | ||
return new Promise(timer).then(fn); | ||
await new Promise(timer); | ||
} | ||
function attempt(fn, ...args) { | ||
return new Promise(accept => accept(fn(...args))); | ||
async function attempt(fn, ...args) { | ||
return await fn(...args); | ||
} | ||
function each(items, limit, iterator) { | ||
async function each(items, limit, iterator) { | ||
if (typeof limit === 'function') { | ||
[limit, iterator] = [Number.POSITIVE_INFINITY, limit]; | ||
} | ||
const length = items.length > limit ? limit : items.length; | ||
let position = length - 1; | ||
const end = items.length - 1; | ||
const chainer = i => { | ||
return attempt(iterator, items[i], i, items) | ||
.then(() => { | ||
return position < end && chainer(++position); | ||
}); | ||
const parts = Array.from(items); | ||
const length = parts.length > limit ? limit : parts.length; | ||
let i = 0; | ||
const chainer = async (position, item) => { | ||
await attempt(iterator, item, position, items); | ||
return parts.length && chainer(i++, parts.shift()); | ||
}; | ||
return Promise.all(Array.from({ length }, (_, n) => chainer(n))); | ||
const simultaneous = Array.from({ length }, (_, n) => chainer(i++, parts.shift())); | ||
await Promise.all(simultaneous); | ||
return items; | ||
} | ||
function map(items, limit, iterator) { | ||
async function map(items, limit, iterator) { | ||
if (typeof limit === 'function') { | ||
[limit, iterator] = [Number.POSITIVE_INFINITY, limit]; | ||
} | ||
const length = items.length > limit ? limit : items.length; | ||
const parts = Array.from(items); | ||
const length = parts.length > limit ? limit : parts.length; | ||
const results = []; | ||
let position = length - 1; | ||
const end = items.length - 1; | ||
const chainer = i => { | ||
return attempt(iterator, items[i], i, items) | ||
.then(result => { | ||
results[i] = result; | ||
if (position < end) { | ||
return chainer(++position); | ||
} | ||
}); | ||
let i = 0; | ||
const chainer = async (position, item) => { | ||
results[position] = await attempt(iterator, item, position, items); | ||
return parts.length && chainer(i++, parts.shift()); | ||
}; | ||
const promises = Array.from({ length }, (_, n) => chainer(n)); | ||
return Promise.all(promises).then(() => results); | ||
const simultaneous = Array.from({ length }, () => chainer(i++, parts.shift())); | ||
await Promise.all(simultaneous); | ||
return results; | ||
} | ||
function reduce(items, iterator, accumulator = []) { | ||
let position = 0; | ||
const end = items.length - 1; | ||
const chainer = (r, i) => { | ||
return attempt(iterator, r, items[i], i, items) | ||
.then(result => { | ||
return position < end ? chainer(result, ++position) : result; | ||
}); | ||
async function reduce(items, iterator, accumulator) { | ||
const parts = Array.from(items); | ||
let i = 0; | ||
const chainer = async (value, item, position) => { | ||
const previous = await attempt(iterator, value, item, position, items); | ||
return parts.length ? chainer(previous, parts.shift(), i++) : previous; | ||
}; | ||
return chainer(accumulator, position); | ||
return await chainer(accumulator, parts.shift(), i++); | ||
} | ||
function auto(tasks) { | ||
async function auto(tasks) { | ||
const keys = Object.keys(tasks); | ||
const promises = {}; | ||
const values = {}; | ||
const creator = key => { | ||
const creator = async key => { | ||
if (promises[key]) return promises[key]; | ||
@@ -76,8 +74,8 @@ | ||
promises[key] = task.reduce((promise, entry) => { | ||
if (typeof entry === 'function') | ||
return promise.then(() => entry(values)); | ||
return promise.then(() => creator(entry)); | ||
if (['string', 'symbol'].includes(typeof entry)) | ||
return promise.then(() => creator(entry)); | ||
return promise.then(() => entry(values)); | ||
}, Promise.resolve()); | ||
} else { | ||
promises[key] = Promise.resolve(values).then(task); | ||
promises[key] = attempt(task, values); | ||
} | ||
@@ -91,10 +89,12 @@ | ||
}; | ||
return Promise.all(keys.map(creator)).then(() => values); | ||
await Promise.all(keys.map(creator)); | ||
return values; | ||
} | ||
function wrap(fn, ...args) { | ||
return new Promise((accept, reject) => { | ||
async function wrap(fn, ...args) { | ||
return await new Promise((accept, reject) => { | ||
fn(...args, (e, ...results) => { | ||
if (e) return void reject(e); | ||
accept(...results); | ||
if (results.length === 1) return accept(results[0]); | ||
accept(results); | ||
}); | ||
@@ -104,57 +104,61 @@ }); | ||
function some(items, limit, iterator) { | ||
async function some(items, limit, iterator) { | ||
if (typeof limit === 'function') { | ||
[limit, iterator] = [Number.POSITIVE_INFINITY, limit]; | ||
} | ||
const errors = []; | ||
const length = items.length > limit ? limit : items.length; | ||
let position = length - 1; | ||
const end = items.length - 1; | ||
return new Promise((accept, reject) => { | ||
let accepted = false; | ||
const complete = v => { | ||
if (accepted) return; | ||
accepted = true; | ||
accept(v); | ||
}; | ||
const chainer = i => attempt(iterator, items[i], i, items).then(complete, (e) => { | ||
errors[i] = e; | ||
return position < end ? chainer(++position) : Promise.resolve(); | ||
}); | ||
Promise.all(Array.from({ length }, (_, n) => chainer(n))).then(() => { | ||
if (accepted) return; | ||
reject(errors); | ||
}); | ||
}); | ||
const parts = Array.from(items); | ||
const length = parts.length > limit ? limit : parts.length; | ||
let i = 0; | ||
let passed = false; | ||
const chainer = async (position, item) => { | ||
if (passed) return true; | ||
passed = await attempt(iterator, item, position, items); | ||
if (passed) return true; | ||
if (parts.length) return chainer(i++, parts.shift()); | ||
}; | ||
const simultaneous = Array.from({ length }, () => chainer(i++, parts.shift())); | ||
await Promise.race(simultaneous); | ||
return !!passed; | ||
} | ||
function every(items, limit, iterator) { | ||
async function every(items, limit, iterator) { | ||
if (typeof limit === 'function') { | ||
[limit, iterator] = [Number.POSITIVE_INFINITY, limit]; | ||
} | ||
const length = items.length > limit ? limit : items.length; | ||
let position = length - 1; | ||
const end = items.length - 1; | ||
const parts = Array.from(items); | ||
const length = parts.length > limit ? limit : parts.length; | ||
let i = 0; | ||
let passing = true; | ||
const chainer = async (position, item) => { | ||
if (!passing) return; | ||
const passed = await attempt(iterator, item, position, items); | ||
passing = passing && !!passed; | ||
if (passing && parts.length) return chainer(i++, parts.shift()); | ||
}; | ||
const simultaneous = Array.from({ length }, () => chainer(i++, parts.shift())); | ||
await Promise.all(simultaneous); | ||
return passing; | ||
} | ||
return new Promise((accept, reject) => { | ||
let failed = false; | ||
const fail = e => { | ||
if (failed) return; | ||
failed = true; | ||
reject(e); | ||
}; | ||
const next = () => { | ||
return position < end && chainer(++position); | ||
}; | ||
const chainer = i => { | ||
return attempt(iterator, items[i], i, items).then(next, fail); | ||
}; | ||
Promise.all(Array.from({ length }, (_, n) => chainer(n))).then(() => { | ||
if (failed) return; | ||
accept(); | ||
}, reject); | ||
function on(target, event, callback) { | ||
const on = target.addEventListener || target.addListener; | ||
const off = target.removeEventListener || target.removeListener; | ||
on.call(target, event, function handler(...args) { | ||
off.call(target, event, handler); | ||
if (args.length === 1) return void callback(args[0]); | ||
callback(args); | ||
}); | ||
} | ||
async function once(events, target, timeout = -1) { | ||
let result; | ||
const simultaneous = [].concat(events).map(event => new Promise(accept => on(target, event, accept))); | ||
if (timeout > -1) { | ||
simultaneous.push(delayed(() => { if (!result) throw new Error('timeout'); }, timeout)); | ||
} | ||
result = await Promise.race(simultaneous); | ||
if (result instanceof Error) throw result; | ||
return result; | ||
} | ||
module.exports = { | ||
@@ -167,6 +171,8 @@ attempt, | ||
map, | ||
once, | ||
reduce, | ||
some, | ||
wait, | ||
wrap | ||
}; | ||
{ | ||
"name": "asynchronously", | ||
"version": "1.1.0", | ||
"version": "2.0.0", | ||
"description": "asynchronously iterate over items and resolve promises instead of callbacks. async library for promises", | ||
@@ -26,5 +26,5 @@ "main": "index.js", | ||
"mocha": "^5.2.0", | ||
"sinon": "^5.0.7" | ||
"sinon": "^7.2.3" | ||
}, | ||
"dependencies": {} | ||
} |
@@ -10,21 +10,29 @@ # asynchronously | ||
- `attempt(fn, ...args)` - Call a function with the given arguments and reject | ||
- `attempt(fn:Function|AsyncFunction, ...args:Array<Any>):Promise<Any>` - Call a function with the given arguments and reject | ||
caught errors | ||
- `auto(tasks)` - Run tasks by specifying dependent additional dependent | ||
- `auto(tasks:Object<String, AsyncFunction|Array<String|AsyncFunction>>):Promise<Object>` - Run tasks by specifying dependent additional dependent | ||
tasks, resolves to an object containing the mapping of task property | ||
names to task result values | ||
- `delayed(fn, ms)` - Execute a function after a given number of milliseconds | ||
and resolve | ||
- `each(items, [limit,] iterator(item, index, array))` - Execute an iterator for a given set of items with a limit | ||
names to task result values. See async.auto for how to construct `tasks` | ||
- `delayed(fn:Function|AsyncFunction, ms:Number = 10):Promise<Any>` - Execute a function after a given number of milliseconds and resolve the promise to the function return value | ||
- `each(items:Iterable, [limit:Number = POSITIVE_INFINITY,] iterator:Function(item:Any, index:Number, items:Iterable)):Promise` - Execute an iterator for a given set of items with a limit | ||
on the number of simultaneous operations | ||
- `every(items, [limit,] iterator(item, index, array))` - Resolve only if the iterator | ||
resolves for each item | ||
- `map(items, [limit,] iterator(item, index, array))` - Collect the results of all items | ||
- `every(items:Iterable, [limit:Number = POSITIVE_INFINITY,] iterator:Function(item:Any, index:Number, items:Iterable)):Promise<Boolean>` - Resolves true if all iterated items resolve true, otherwise resolves false | ||
- `map(items:Iterable, [limit:Number = POSITIVE_INFINITY,] iterator:Function(item:Any, index:Number, items:Iterable))` - Collect the results of all items | ||
passed to the iterator | ||
- `reduce(items, iterator(previous, item, index, array), accumulator = [])` - Sequentially call an | ||
- `reduce(items:Iterable, iterator(previous:Any, item:Any, index:Number, items:Iterable), accumulator:Any)` - Sequentially call an | ||
iterator for each item and include the previous result starting with | ||
the value of the accumulator | ||
- `some(items, [limit,] iterator(item, index, array))` - Resolve the first item that | ||
resolves for the iterator, rejects if all reject | ||
- `wrap(fn, ...args)` - Call a function using node callback style and | ||
- `some(items:Iterable, [limit:Number = POSITIVE_INFINITY,] iterator:Function(item:Any, index:Number, items:Iterable)):Promise<Boolean>` - Resolves true if any iterated items resolve true, otherwise resolves false | ||
- `wrap(fn:Function, ...args:Array<Any>):Promise<Any>` - Call a function using node callback style and | ||
resolve or reject to a promise | ||
- `once(event:String|Array<String>, target:EventEmitter, timeout:Number = -1):Promise<Any>` - Wait for one or more events | ||
on an EventEmitter and optionally timeout the wait | ||
- `wait(ms:Number = 10)` - Use a promise for a setTimeout call | ||
### Changelog | ||
- v2.0.0 | ||
* Use async / await where possible | ||
* Change behavior in every/some where errors would be swallowed | ||
* Add `once` method |
'use strict'; | ||
const { EventEmitter } = require('events'); | ||
const assume = require('assume'); | ||
@@ -8,3 +10,3 @@ const { stub } = require('sinon'); | ||
const { delayed, each, map, reduce, auto, wrap, attempt, some, every } = require('../'); | ||
const { delayed, each, map, reduce, auto, wrap, attempt, some, every, once } = require('../'); | ||
@@ -26,17 +28,16 @@ const delay = (ms = 10) => new Promise(a => setTimeout(a, ms)); | ||
describe('some', function () { | ||
it('should reject if all items reject', () => { | ||
return some([1, 2, 3, 4, 5], i => { throw new Error(`${i} error`); }) | ||
.catch(errors => { | ||
assume(errors.length).equals(5); | ||
assume(errors[0].message).equals('1 error'); | ||
assume(errors[1].message).equals('2 error'); | ||
assume(errors[2].message).equals('3 error'); | ||
assume(errors[3].message).equals('4 error'); | ||
assume(errors[4].message).equals('5 error'); | ||
}); | ||
it('should not catch errors in the iterator', async () => { | ||
const error = new Error('intentional error'); | ||
try { | ||
await some([1, 2, 3, 4, 5], i => { throw error; }); | ||
throw new Error('this should not be called'); | ||
} catch (err) { | ||
assume(err).equals(error); | ||
} | ||
}); | ||
it('should only process the limit of items simultaneously', () => { | ||
it('should only process the limit of items simultaneously', async () => { | ||
const resolved = []; | ||
return some([1, 2, 3, 4, 5], 3, i => { | ||
return await some([1, 2, 3, 4, 5], 3, i => { | ||
if (i === 3) assume(resolved.length).eq(0); | ||
if (i === 4) assume(resolved.length).gt(0); | ||
@@ -48,8 +49,8 @@ if (i === 5) assume(resolved.length).gt(1); | ||
it('should resolve if any items resolve', () => { | ||
return some([1, 2, 3, 4, 5], i => i).then(v => assume(v).equals(1)); | ||
it('should resolve if any items resolve', async () => { | ||
return assume(await some([1, 2, 3, 4, 5], i => i)).equals(true); | ||
}); | ||
it('should race to completion', () => { | ||
return some([1, 2, 3, 4, 5], i => i === 3 ? i : delay(i*100)).then(v => assume(v).equals(3)); | ||
it('should race to completion', async () => { | ||
return assume(await some([1, 2, 3, 4, 5], i => i === 3 ? i : delay(i*100))).equals(true); | ||
}); | ||
@@ -59,5 +60,10 @@ }); | ||
describe('every', function () { | ||
it('should reject if any item rejects', () => { | ||
it('should throw any errors', async () => { | ||
const error = new Error('reject on purpose'); | ||
return every([1, 2, 3], i => { throw error; }).catch(e => assume(error).equals(e)); | ||
try { | ||
await every([1, 2, 3], i => { throw error; }); | ||
throw new Error('this should not be called'); | ||
} catch (err) { | ||
assume(err).equals(error); | ||
} | ||
}); | ||
@@ -68,2 +74,3 @@ | ||
return every([1, 2, 3, 4, 5], 3, i => { | ||
if (i === 3) assume(resolved.length).eq(0); | ||
if (i === 4) assume(resolved.length).gt(0); | ||
@@ -81,28 +88,24 @@ if (i === 5) assume(resolved.length).gt(1); | ||
describe('each', function () { | ||
it('should call the iterator for each item in the array', () => { | ||
it('should call the iterator for each item in the array', async () => { | ||
const iterator = stub().resolves('yes'); | ||
return each([1, 2, 3], 2, iterator) | ||
.then(() => { | ||
assume(iterator).called(3); | ||
assume(iterator).calledWith(1); | ||
assume(iterator).calledWith(2); | ||
assume(iterator).calledWith(3); | ||
}); | ||
await each([1, 2, 3], 2, iterator); | ||
assume(iterator).called(3); | ||
assume(iterator).calledWith(1); | ||
assume(iterator).calledWith(2); | ||
assume(iterator).calledWith(3); | ||
}); | ||
it('should default the limit to 5 or the length of items if not specified', () => { | ||
it('should default the limit to 5 or the length of items if not specified', async () => { | ||
const iterator = stub().resolves('yes'); | ||
return each([1, 2, 3], iterator) | ||
.then(() => { | ||
assume(iterator).called(3); | ||
assume(iterator).calledWith(1); | ||
assume(iterator).calledWith(2); | ||
assume(iterator).calledWith(3); | ||
}); | ||
await each([1, 2, 3], iterator); | ||
assume(iterator).called(3); | ||
assume(iterator).calledWith(1); | ||
assume(iterator).calledWith(2); | ||
assume(iterator).calledWith(3); | ||
}); | ||
it('should wait until the one of the limited iterations has completed before continuing', () => { | ||
it('should wait until the one of the limited iterations has completed before continuing', async () => { | ||
const resolved = {}; | ||
const base = [1, 2, 3, 4, 5]; | ||
return each(base, 2, function (arg, index, array) { | ||
await each(base, 2, function (arg, index, array) { | ||
assume(array).equals(base); | ||
@@ -120,10 +123,8 @@ assume(index).to.be.lessThan(array.length); | ||
return delay(10 * arg).then(() => { resolved[arg] = true; }); | ||
}) | ||
.then(() => { | ||
assume(resolved[1]).to.equal(true); | ||
assume(resolved[2]).to.equal(true); | ||
assume(resolved[3]).to.equal(true); | ||
assume(resolved[4]).to.equal(true); | ||
assume(resolved[5]).to.equal(true); | ||
}); | ||
}); | ||
assume(resolved[1]).to.equal(true); | ||
assume(resolved[2]).to.equal(true); | ||
assume(resolved[3]).to.equal(true); | ||
assume(resolved[4]).to.equal(true); | ||
assume(resolved[5]).to.equal(true); | ||
}); | ||
@@ -133,37 +134,31 @@ }); | ||
describe('map', function () { | ||
it('should collect the results in a new array', () => { | ||
return map([1, 2, 3, 4, 5], 3, a => a * 2) | ||
.then(result => { | ||
assume(Array.isArray(result)).to.equal(true); | ||
assume(result.length).to.equal(5); | ||
assume(result).to.deep.equal([2, 4, 6, 8, 10]); | ||
}); | ||
it('should collect the results in a new array', async () => { | ||
const result = await map([1, 2, 3, 4, 5], 3, a => a * 2); | ||
assume(Array.isArray(result)).to.equal(true); | ||
assume(result.length).to.equal(5); | ||
assume(result).to.deep.equal([2, 4, 6, 8, 10]); | ||
}); | ||
it('should default the limit to 5 or the length of items if not specified', () => { | ||
it('should default the limit to 5 or the length of items if not specified', async () => { | ||
const iterator = stub().resolves('yes'); | ||
return map([1, 2, 3], iterator) | ||
.then(() => { | ||
assume(iterator).calledThrice; | ||
assume(iterator).calledWith(1); | ||
assume(iterator).calledWith(2); | ||
assume(iterator).calledWith(3); | ||
}); | ||
await map([1, 2, 3], iterator); | ||
assume(iterator).calledThrice; | ||
assume(iterator).calledWith(1); | ||
assume(iterator).calledWith(2); | ||
assume(iterator).calledWith(3); | ||
}); | ||
it('should call the iterator for each item in the array', () => { | ||
it('should call the iterator for each item in the array', async () => { | ||
const iterator = stub().resolves('yes'); | ||
return map([1, 2, 3], 2, iterator) | ||
.then(() => { | ||
assume(iterator).calledThrice; | ||
assume(iterator).calledWith(1); | ||
assume(iterator).calledWith(2); | ||
assume(iterator).calledWith(3); | ||
}); | ||
await map([1, 2, 3], 2, iterator); | ||
assume(iterator).calledThrice; | ||
assume(iterator).calledWith(1); | ||
assume(iterator).calledWith(2); | ||
assume(iterator).calledWith(3); | ||
}); | ||
it('should wait until the one of the limited iterations has completed before continuing', () => { | ||
it('should wait until the one of the limited iterations has completed before continuing', async () => { | ||
const resolved = {}; | ||
const base = [1, 2, 3, 4, 5]; | ||
return map(base, 2, function (arg, index, array) { | ||
const result = await map(base, 2, function (arg, index, array) { | ||
assume(array).equals(base); | ||
@@ -181,13 +176,11 @@ assume(index).to.be.lessThan(array.length); | ||
return delay(10 * arg).then(() => { resolved[arg] = true; }).then(() => arg * 2); | ||
}) | ||
.then(result => { | ||
assume(resolved[1]).to.equal(true); | ||
assume(resolved[2]).to.equal(true); | ||
assume(resolved[3]).to.equal(true); | ||
assume(resolved[4]).to.equal(true); | ||
assume(resolved[5]).to.equal(true); | ||
assume(Array.isArray(result)).to.equal(true); | ||
assume(result.length).to.equal(5); | ||
assume(result).to.deep.equal([2, 4, 6, 8, 10]); | ||
}); | ||
}); | ||
assume(resolved[1]).to.equal(true); | ||
assume(resolved[2]).to.equal(true); | ||
assume(resolved[3]).to.equal(true); | ||
assume(resolved[4]).to.equal(true); | ||
assume(resolved[5]).to.equal(true); | ||
assume(Array.isArray(result)).to.equal(true); | ||
assume(result.length).to.equal(5); | ||
assume(result).to.deep.equal([2, 4, 6, 8, 10]); | ||
}); | ||
@@ -197,7 +190,7 @@ }); | ||
describe('reduce', function () { | ||
it('should pass the accumulator value in each iteration', () => { | ||
it('should pass the accumulator value in each iteration', async () => { | ||
const resolved = {}; | ||
const base = [1, 2, 3, 4, 5]; | ||
const accumulator = []; | ||
return reduce(base, function (memo, arg, index, array) { | ||
const result = await reduce(base, function (memo, arg, index, array) { | ||
assume(memo).to.equal(accumulator); | ||
@@ -213,13 +206,11 @@ assume(array).to.equal(base); | ||
}); | ||
}, accumulator) | ||
.then(result => { | ||
assume(resolved[1]).to.equal(true); | ||
assume(resolved[2]).to.equal(true); | ||
assume(resolved[3]).to.equal(true); | ||
assume(resolved[4]).to.equal(true); | ||
assume(resolved[5]).to.equal(true); | ||
assume(Array.isArray(result)).to.equal(true); | ||
assume(result.length).to.equal(5); | ||
assume(result).to.deep.equal([2, 4, 6, 8, 10]); | ||
}); | ||
}, accumulator); | ||
assume(resolved[1]).to.equal(true); | ||
assume(resolved[2]).to.equal(true); | ||
assume(resolved[3]).to.equal(true); | ||
assume(resolved[4]).to.equal(true); | ||
assume(resolved[5]).to.equal(true); | ||
assume(Array.isArray(result)).to.equal(true); | ||
assume(result.length).to.equal(5); | ||
assume(result).to.deep.equal([2, 4, 6, 8, 10]); | ||
}); | ||
@@ -229,11 +220,13 @@ }); | ||
describe('auto', function () { | ||
it('should pass the result of each task to the next tasks', () => { | ||
return auto({ | ||
const insertion = Symbol.for('test'); | ||
it('should pass the result of each task to the next tasks', async () => { | ||
const result = await auto({ | ||
first() { | ||
return Promise.resolve('first-done'); | ||
}, | ||
second: ['first', ({ first, fourth }) => { | ||
second: ['first', async ({ first, fourth }) => { | ||
// fourth depends on second and will not evaluate until second is resolved | ||
assume(fourth).to.be.undefined; | ||
assume(first).to.equal('first-done'); | ||
await delayed(() => {}, 100); | ||
return 'second-done'; | ||
@@ -245,10 +238,15 @@ }], | ||
}], | ||
fourth: ['second', ({ second }) => { | ||
[insertion]() { | ||
return 'called'; | ||
}, | ||
fourth: ['second', insertion, async ({ second, [insertion]: insert }) => { | ||
assume(second).to.equal('second-done'); | ||
return Promise.resolve('fourth-done'); | ||
assume(insert).equals('called'); | ||
return 'fourth-done'; | ||
}] | ||
}).then(result => { | ||
assume(Object.keys(result)).to.deep.equal(['first', 'second', 'fourth', 'third']); | ||
assume(Object.values(result)).to.deep.equal(['first-done', 'second-done', 'fourth-done', 'third-done']); | ||
}); | ||
assume(result[insertion]).equals('called'); | ||
assume(Object.keys(result)).to.deep.equal(['first', 'second', 'fourth', 'third']); | ||
assume(Object.values(result)).to.deep.equal(['first-done', 'second-done', 'fourth-done', 'third-done']); | ||
}); | ||
@@ -258,28 +256,54 @@ }); | ||
describe('wrap', function () { | ||
it('should wrap a callback function into a promise', () => { | ||
return wrap(cb => setTimeout(() => cb(null, 123), 10)) | ||
.then(result => { | ||
assume(result).to.equal(123); | ||
}); | ||
it('should wrap a callback function into a promise', async () => { | ||
const result = await wrap(cb => setTimeout(() => cb(null, 123), 10)); | ||
assume(result).to.equal(123); | ||
}); | ||
it('should wrap a callback function into a promise and reject an error', () => { | ||
return wrap(cb => setTimeout(() => cb(new Error('expected')), 10)) | ||
.then(() => { | ||
throw new Error('this should not have been called'); | ||
}, err => { | ||
assume(err.message).to.equal('expected'); | ||
}); | ||
it('should wrap a callback function into a promise and reject an error', async () => { | ||
try { | ||
await wrap(cb => setTimeout(() => cb(new Error('expected')), 10)) | ||
throw new Error('this should not have been called'); | ||
} catch (err) { | ||
assume(err.message).equals('expected'); | ||
} | ||
}); | ||
it('should reject on error', () => { | ||
return wrap(cb => setTimeout(() => cb(new Error('expected')), 10)) | ||
.then(() => { | ||
throw new Error('this should not have been called'); | ||
}, err => { | ||
assume(err.message).to.equal('expected'); | ||
}); | ||
it('should reject on error', async () => { | ||
try { | ||
await wrap(cb => setTimeout(() => cb(new Error('expected')), 10)); | ||
throw new Error('this should not have been called'); | ||
} catch (err) { | ||
assume(err.message).to.equal('expected'); | ||
} | ||
}); | ||
}); | ||
describe('once', function () { | ||
it('should resolve the promise with the yielded argument', async () => { | ||
const emitter = new EventEmitter(); | ||
setTimeout(() => emitter.emit('test', 123)); | ||
const result = await once('test', emitter); | ||
assume(result).equals(123); | ||
}); | ||
it('should resolve the promise with the yielded arguments', async () => { | ||
const emitter = new EventEmitter(); | ||
setTimeout(() => emitter.emit('test', 123, 'abc')); | ||
const result = await once('test', emitter); | ||
assume(Array.isArray(result)).equals(true); | ||
assume(result[0]).equals(123); | ||
assume(result[1]).equals('abc'); | ||
}); | ||
it('should throw an error on timeout', async () => { | ||
const emitter = new EventEmitter(); | ||
try { | ||
await once('test', emitter, 10); | ||
assume(false).equals(true); | ||
} catch (e) { | ||
assume(e.message).equals('timeout'); | ||
} | ||
}); | ||
}); | ||
}); | ||
19759
15.39%421
6.05%38
26.67%