You're Invited: Meet the Socket team at BSidesSF and RSAC - April 27 - May 1.RSVP

asynchronously

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

asynchronously - npm Package Compare versions

Comparing version

to
2.0.0

'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');
}
});
});
});