UNPKG

insync

Version:

Higher-order functions and common patterns for asynchronous code. Node specific port of async.

1,675 lines (1,175 loc) 143 kB
'use strict'; // Load Modules var Code = require('code'); var Lab = require('lab'); var lab = exports.lab = Lab.script(); var Insync = require('../lib'); var Util = require('../lib/util'); // Declare internals var internals = {}; internals.eachAsync = function (result) { return function (item, callback) { setTimeout(function () { result.push(item); callback(null); }, item * 100); }; }; internals.eachOfAsync = function (result) { return function (value, key, callback) { setTimeout(function () { result.push([value, key]); callback(null); }, value * 100); }; }; internals.mapAsync = function (result) { return function (item, callback) { setTimeout(function () { var dbl = item + item; result.push(dbl); callback(null, dbl); }, item * 100); }; }; internals.filterAsync = function (callOrder) { return function (item, callback) { callOrder.push(item); setTimeout(function () { callback(null, item > 1); }, item * 100); }; }; internals.reduceAsync = function (callOrder) { return function (memo, item, callback) { callOrder.push(item); setTimeout(function () { callback(null, memo + item); }); }; }; internals.doNothing = function () { var args = Array.prototype.slice.call(arguments); var callback = args.pop(); setTimeout(function () { callback(null); }, 100); }; // Test aliases var describe = lab.describe; var it = lab.it; var before = lab.before; var after = lab.after; var expect = Code.expect; describe('Insync', function () { describe('Collection', function () { describe('each()', function () { it('iterates over a collection of items in parallel', function (done) { var result = []; Insync.each([4, 3, 2, 1], internals.eachAsync(result), function (error) { expect(error).to.not.exist(); expect(result).to.deep.equal([1, 2, 3, 4]); done(); }); }); it('provides a default callback if one is not provided', function (done) { var noop = Util.noop; Util.noop = function (error) { Util.noop = noop; expect(error).to.not.exist(); done(); }; Insync.each([4], internals.doNothing); }); it('short circuits if the array is empty', function (done) { Insync.each([], Util.noop, function (error) { expect(error).to.not.exist(); done(); }); }); it('sends an error when it occurs', function (done) { Insync.each([1], function (item, callback) { setTimeout(function () { callback(new Error('async error')); }); }, function (error) { expect(error).to.exist(); expect(error.message).to.equal('async error'); done(); }); }); it('is aliased as forEach()', function (done) { expect(Insync.each).to.equal(Insync.forEach); done(); }); }); describe('eachSeries()', function () { it('iterates over a collection of items in series', function (done) { var result = []; Insync.eachSeries([1, 3, 2], internals.eachAsync(result), function (error) { expect(error).to.not.exist(); expect(result).to.deep.equal([1, 3, 2]); done(); }); }); it('provides a default callback if one is not provided', function (done) { var noop = Util.noop; Util.noop = function (error) { Util.noop = noop; expect(error).to.not.exist(); done(); }; Insync.eachSeries([1], internals.doNothing); }); it('short circuits if the array is empty', function (done) { Insync.eachSeries([], Util.noop, function (error) { expect(error).to.not.exist(); done(); }); }); it('sends an error when it occurs', function (done) { Insync.eachSeries([1], function (item, callback) { setTimeout(function () { callback(new Error('async error')); }); }, function (error) { expect(error).to.exist(); expect(error.message).to.equal('async error'); done(); }); }); it('is aliased as forEachSeries()', function (done) { expect(Insync.eachSeries).to.equal(Insync.forEachSeries); done(); }); }); describe('eachLimit()', function () { it('iterates over a collection with a limit', function (done) { var arr = [0, 1, 2, 3]; var result = []; Insync.eachLimit(arr, 2, internals.eachAsync(result), function (err) { expect(err).to.not.exist(); expect(result).to.deep.equal(arr); done(); }); }); it('does not call iterator if array is empty', function (done) { Insync.eachLimit([], 2, function (item, callback) { expect(true).to.equal(false); }, function (err) { expect(err).to.not.exist(); done(); }); }); it('does not call iterator if limit is zero', function (done) { Insync.eachLimit([1, 2, 3], 0, function (item, callback) { expect(true).to.equal(false); }, function (err) { expect(err).to.not.exist(); done(); }); }); it('properly passes error to final callback', function (done) { var arr = [0, 1, 2, 3]; var result = []; Insync.eachLimit(arr, 2, function (item, callback) { result.push(item); if (item === 2) { return callback(new Error('foo')); } callback(); }, function (err) { expect(err).to.exist(); expect(result).to.deep.equal([0, 1, 2]); done(); }); }); it('provides a default callback if one is not provided', function (done) { var noop = Util.noop; Util.noop = function (error) { Util.noop = noop; expect(error).to.not.exist(); done(); }; Insync.eachLimit([1, 2, 3, 4], 2, internals.doNothing); }); it('is aliased as forEachLimit()', function (done) { expect(Insync.eachLimit).to.equal(Insync.forEachLimit); done(); }); }); describe('eachOf()', function () { it('iterates over an array in parallel', function (done) { var result = []; Insync.eachOf([4, 3, 2, 1], internals.eachOfAsync(result), function (error) { expect(error).to.not.exist(); expect(result).to.deep.equal([ [1, 3], [2, 2], [3, 1], [4, 0] ]); done(); }); }); it('iterates over an object in parallel', function (done) { var result = []; Insync.eachOf({ a: 4, b: 3, c: 2, d: 1 }, internals.eachOfAsync(result), function (error) { expect(error).to.not.exist(); expect(result).to.deep.equal([ [1, 'd'], [2, 'c'], [3, 'b'], [4, 'a'] ]); done(); }); }); it('provides a default callback if one is not provided', function (done) { var noop = Util.noop; Util.noop = function (error) { Util.noop = noop; expect(error).to.not.exist(); done(); }; Insync.eachOf([4], internals.doNothing); }); it('short circuits if the array is empty', function (done) { Insync.eachOf([], function (value, key, callback) { expect(true).to.equal(false); callback(); }, function (error) { expect(error).to.not.exist(); done(); }); }); it('sends an error when it occurs', function (done) { Insync.eachOf([1], function (value, key, callback) { setTimeout(function () { callback(new Error('async error')); }); }, function (error) { expect(error).to.exist(); expect(error.message).to.equal('async error'); done(); }); }); it('is aliased as forEachOf()', function (done) { expect(Insync.eachOf).to.equal(Insync.forEachOf); done(); }); }); describe('eachOfSeries()', function () { it('iterates over an array in series', function (done) { var result = []; Insync.eachOfSeries([1, 3, 2], internals.eachOfAsync(result), function (error) { expect(error).to.not.exist(); expect(result).to.deep.equal([ [1, 0], [3, 1], [2, 2] ]); done(); }); }); it('iterates over an object in series', function (done) { var result = []; Insync.eachOfSeries({ a: 4, b: 3, c: 2, d: 1 }, internals.eachOfAsync(result), function (error) { expect(error).to.not.exist(); expect(result).to.deep.equal([ [4, 'a'], [3, 'b'], [2, 'c'], [1, 'd'] ]); done(); }); }); it('provides a default callback if one is not provided', function (done) { var noop = Util.noop; Util.noop = function (error) { Util.noop = noop; expect(error).to.not.exist(); done(); }; Insync.eachOfSeries([1], internals.doNothing); }); it('short circuits if the array is empty', function (done) { Insync.eachOfSeries([], Util.noop, function (error) { expect(error).to.not.exist(); done(); }); }); it('sends an error when it occurs', function (done) { Insync.eachOfSeries([1], function (value, key, callback) { setTimeout(function () { callback(new Error('async error')); }); }, function (error) { expect(error).to.exist(); expect(error.message).to.equal('async error'); done(); }); }); it('is aliased as forEachOfSeries()', function (done) { expect(Insync.eachOfSeries).to.equal(Insync.forEachOfSeries); done(); }); }); describe('eachOfLimit()', function () { it('iterates over an array with a limit', function (done) { var arr = [0, 1, 2, 3]; var result = []; Insync.eachOfLimit(arr, 2, internals.eachOfAsync(result), function (err) { expect(err).to.not.exist(); expect(result).to.deep.equal([ [0, 0], [1, 1], [2, 2], [3, 3] ]); done(); }); }); it('iterates over an object with a limit', function (done) { var obj = { a: 0, b: 1, c: 2, d: 3 }; var result = []; Insync.eachOfLimit(obj, 2, internals.eachOfAsync(result), function (err) { expect(err).to.not.exist(); expect(result).to.deep.equal([ [0, 'a'], [1, 'b'], [2, 'c'], [3, 'd'] ]); done(); }); }); it('does not call iterator if array is empty', function (done) { Insync.eachOfLimit([], 2, function (value, key, callback) { expect(true).to.equal(false); }, function (err) { expect(err).to.not.exist(); done(); }); }); it('does not call iterator if limit is zero', function (done) { Insync.eachOfLimit([1, 2, 3], 0, function (value, key, callback) { expect(true).to.equal(false); }, function (err) { expect(err).to.not.exist(); done(); }); }); it('properly passes error to final callback', function (done) { var arr = [0, 1, 2, 3]; var result = []; Insync.eachOfLimit(arr, 2, function (value, key, callback) { result.push([value, key]); if (value === 2) { return callback(new Error('foo')); } callback(); }, function (err) { expect(err).to.exist(); expect(result).to.deep.equal([ [0, 0], [1, 1], [2, 2] ]); done(); }); }); it('provides a default callback if one is not provided', function (done) { var noop = Util.noop; Util.noop = function (error) { Util.noop = noop; expect(error).to.not.exist(); done(); }; Insync.eachOfLimit([1, 2, 3, 4], 2, internals.doNothing); }); it('is aliased as forEachOfLimit()', function (done) { expect(Insync.eachOfLimit).to.equal(Insync.forEachOfLimit); done(); }); }); describe('map()', function () { it('creates a mapped version of input', function (done) { var arr = [0, 1, 2, 3]; var result = []; Insync.map(arr, internals.mapAsync(result), function (err, mapped) { expect(err).to.not.exist(); expect(arr).to.deep.equal([0, 1, 2, 3]); expect(mapped).to.deep.equal(result); done(); }); }); it('works without final callback', function (done) { var arr = [0, 1, 2, 3]; var result = []; Insync.map(arr, function (item, callback) { result.push(item); if (result.length === arr.length) { expect(arr).to.deep.equal(result); done(); } callback(null, item); }); }); it('properly passes error to final callback', function (done) { var arr = [0, 1, 2, 3]; var result = []; Insync.map(arr, function (item, callback) { result.push(item); if (item === 2) { return callback(new Error('foo')); } callback(); }, function (err) { expect(err).to.exist(); expect(result).to.deep.equal([0, 1, 2]); done(); }); }); }); describe('mapSeries()', function () { it('creates a mapped version of input', function (done) { var arr = [0, 1, 2, 3]; var result = []; Insync.mapSeries(arr, internals.mapAsync(result), function (err, mapped) { expect(err).to.not.exist(); expect(arr).to.deep.equal([0, 1, 2, 3]); expect(mapped).to.deep.equal(result); done(); }); }); it('properly passes error to final callback', function (done) { var arr = [0, 1, 2, 3]; var result = []; Insync.mapSeries(arr, function (item, callback) { result.push(item); if (item === 2) { return callback(new Error('foo')); } callback(); }, function (err) { expect(err).to.exist(); expect(result).to.deep.equal([0, 1, 2]); done(); }); }); }); describe('mapLimit()', function () { it('iterates over a collection with a limit', function (done) { var arr = [0, 1, 2, 3]; var result = []; Insync.mapLimit(arr, 2, internals.mapAsync(result), function (err, mapped) { expect(err).to.not.exist(); expect(result).to.deep.equal(mapped); done(); }); }); }); describe('filter()', function () { it('filters an array', function (done) { var arr = [0, 1, 2, 3]; var callOrder = []; Insync.filter(arr, internals.filterAsync(callOrder), function (err, results) { expect(err).not.to.exist(); expect(arr).to.deep.equal([0, 1, 2, 3]); expect(results).to.deep.equal([2, 3]); done(); }); }); it('handles errors', function (done) { var doneCalled = false; Insync.filter([0, 1, 2], function (item, callback) { setTimeout(function () { callback(new Error('foo')); }, 50); }, function (err, results) { expect(err).to.exist(); expect(results).to.not.exist(); expect(doneCalled).to.equal(false); doneCalled = true; setTimeout(done, 100); }); }); it('is aliased as select()', function (done) { expect(Insync.filter).to.equal(Insync.select); done(); }); }); describe('filterSeries()', function () { it('filters an array in series', function (done) { var arr = [0, 1, 2, 3]; var callOrder = []; Insync.filterSeries(arr, internals.filterAsync(callOrder), function (err, results) { expect(err).not.to.exist(); expect(arr).to.deep.equal([0, 1, 2, 3]); expect(callOrder).to.deep.equal(arr); expect(results).to.deep.equal([2, 3]); done(); }); }); it('is aliased as selectSeries()', function (done) { expect(Insync.filterSeries).to.equal(Insync.selectSeries); done(); }); }); describe('reject()', function () { it('filters an array', function (done) { var arr = [0, 1, 2, 3]; var callOrder = []; Insync.reject(arr, internals.filterAsync(callOrder), function (err, results) { expect(err).to.not.exist(); expect(arr).to.deep.equal([0, 1, 2, 3]); expect(results).to.deep.equal([0, 1]); done(); }); }); it('handles errors', function (done) { var doneCalled = false; Insync.reject([0, 1, 2], function (item, callback) { setTimeout(function () { callback(new Error('foo')); }, 50); }, function (err, results) { expect(err).to.exist(); expect(results).to.not.exist(); expect(doneCalled).to.equal(false); doneCalled = true; setTimeout(done, 100); }); }); }); describe('rejectSeries()', function () { it('filters an array in series', function (done) { var arr = [0, 1, 2, 3]; var callOrder = []; Insync.rejectSeries(arr, internals.filterAsync(callOrder), function (err, results) { expect(err).to.not.exist(); expect(arr).to.deep.equal([0, 1, 2, 3]); expect(callOrder).to.deep.equal(arr); expect(results).to.deep.equal([0, 1]); done(); }); }); }); describe('reduce()', function () { it('reduces an array', function (done) { var arr = [0, 1, 2, 3]; var callOrder = []; Insync.reduce(arr, 0, internals.reduceAsync(callOrder), function (err, result) { expect(err).to.not.exist(); expect(result).to.equal(6); expect(arr).to.deep.equal([0, 1, 2, 3]); expect(callOrder).to.deep.equal(arr); done(); }); }); it('handles errors', function (done) { var doneCalled = false; Insync.reduce([1, 2, 3], 0, function (a, x, callback) { callback(new Error()); }, function (err) { expect(err).to.exist(); expect(doneCalled).to.equal(false); doneCalled = true; setTimeout(done, 100); }); }); it('is aliased as inject()', function (done) { expect(Insync.reduce).to.equal(Insync.inject); done(); }); it('is aliased as foldl()', function (done) { expect(Insync.reduce).to.equal(Insync.foldl); done(); }); }); describe('reduceRight()', function () { it('reduces an array', function (done) { var arr = [0, 1, 2, 3]; var callOrder = []; Insync.reduceRight(arr, 0, internals.reduceAsync(callOrder), function (err, result) { expect(err).to.not.exist(); expect(result).to.equal(6); expect(arr).to.deep.equal([0, 1, 2, 3]); expect(callOrder).to.deep.equal([3, 2, 1, 0]); done(); }); }); it('is aliased as foldr()', function (done) { expect(Insync.reduceRight).to.equal(Insync.foldr); done(); }); }); describe('detect()', function () { it('returns the first value that passes test', function (done) { var arr = [0, 1, 2, 3]; var callOrder = []; Insync.detect(arr, function (item, callback) { callOrder.push(item); setTimeout(function () { callback(null, item === 2); }, 100); }, function (err, result) { expect(err).to.not.exist(); expect(result).to.equal(2); done(); }); }); it('returns undefined when no items pass test', function (done) { var arr = [0, 1, 2, 3]; var callOrder = []; Insync.detect(arr, function (item, callback) { callOrder.push(item); setTimeout(function () { callback(null, item === 6); }, 100); }, function (err, result) { expect(err).to.not.exist(); expect(result).to.equal(undefined); done(); }); }); it('handles errors', function (done) { var arr = [0, 1, 2, 3]; Insync.detect(arr, function (item, callback) { setTimeout(function () { callback(new Error('foo')); }, 100); }, function (err, result) { expect(err).to.exist(); expect(result).to.not.exist(); done(); }); }); }); describe('detectSeries()', function () { it('detects in series', function (done) { var callOrder = []; Insync.detectSeries([3, 2, 1], function (item, callback) { callOrder.push(item); setTimeout(function () { callback(null, item === 2); }, 10); }, function (err, result) { expect(err).to.not.exist(); expect(result).to.equal(2); expect(callOrder).to.deep.equal([3, 2]); done(); }); }); it('stops on a match', function (done) { Insync.detectSeries([1, 2, 3, 4], function (item, callback) { expect(item).to.be.at.most(3); callback(null, item === 3); }, function (err, result) { expect(err).to.not.exist(); expect(result).to.equal(3); done(); }); }); it('handles errors', function (done) { var arr = [0, 1, 2, 3]; Insync.detectSeries(arr, function (item, callback) { setTimeout(function () { callback(new Error('foo')); }, 100); }, function (err, result) { expect(err).to.exist(); expect(result).to.not.exist(); done(); }); }); }); describe('some()', function () { it('returns true if at least one element satisfies test', function (done) { var arr = [0, 1, 2, 3]; Insync.some(arr, function (item, callback) { setTimeout(function () { callback(null, item === 2); }, 100); }, function (err, result) { expect(err).to.not.exist(); expect(result).to.equal(true); done(); }); }); it('returns false if no elements satisfies test', function (done) { var arr = [0, 1, 2, 3]; Insync.some(arr, function (item, callback) { setTimeout(function () { callback(null, item === 6); }, 100); }, function (err, result) { expect(err).to.not.exist(); expect(result).to.equal(false); done(); }); }); it('returns early on match', function (done) { var callOrder = []; Insync.some([1, 2, 3], function (item, callback) { setTimeout(function () { callOrder.push(item); callback(null, item === 1); }, item * 25); }, function (err, result) { expect(err).to.not.exist(); expect(result).to.equal(true); expect(callOrder).to.deep.equal([1]); // note that 2 and 3 are still executed done(); }); }); it('handles errors', function (done) { Insync.some([1, 2, 3], function (item, callback) { setTimeout(function () { callback(new Error('foo')); }, 100); }, function (err, result) { expect(err).to.exist(); expect(result).to.not.exist(); done(); }); }); it('is aliased as any()', function (done) { expect(Insync.some).to.equal(Insync.any); done(); }); }); describe('every()', function () { it('returns true if every element satisfies test', function (done) { var arr = [0, 1, 2, 3]; Insync.every(arr, function (item, callback) { setTimeout(function () { callback(null, item >= 0 && item < 4); }, 100); }, function (err, result) { expect(err).to.not.exist(); expect(result).to.equal(true); done(); }); }); it('returns false if any element fails test', function (done) { var arr = [0, 1, 2, 3]; Insync.every(arr, function (item, callback) { setTimeout(function () { callback(null, item >= 1); }, 100); }, function (err, result) { expect(err).to.not.exist(); expect(result).to.equal(false); done(); }); }); it('returns early on failure', function (done) { var callOrder = []; Insync.every([1, 2, 3], function (item, callback) { setTimeout(function () { callOrder.push(item); callback(null, item === 1); }, item * 25); }, function (err, result) { expect(err).to.not.exist(); expect(result).to.equal(false); expect(callOrder).to.deep.equal([1, 2]); // note that 3 is still executed done(); }); }); it('handles errors', function (done) { Insync.every([1, 2, 3], function (item, callback) { setTimeout(function () { callback(new Error('foo')); }, 100); }, function (err, result) { expect(err).to.exist(); expect(result).to.not.exist(); done(); }); }); it('is aliased as all()', function (done) { expect(Insync.every).to.equal(Insync.all); done(); }); }); describe('concat()', function () { it('concatenates results', function (done) { var arr = [0, 1, 2, 3]; Insync.concat(arr, function (item, callback) { setTimeout(function () { callback(null, [item, item]); }, 100); }, function (err, results) { expect(err).to.not.exist(); expect(arr).to.deep.equal([0, 1, 2, 3]); expect(results).to.deep.equal([0, 0, 1, 1, 2, 2, 3, 3]); done(); }); }); it('creates empty array when iterator returns no results', function (done) { var arr = [0, 1, 2, 3]; Insync.concat(arr, function (item, callback) { setTimeout(function () { callback(null); }, 100); }, function (err, results) { expect(err).to.not.exist(); expect(arr).to.deep.equal([0, 1, 2, 3]); expect(results).to.deep.equal([]); done(); }); }); }); describe('concatSeries()', function () { it('concatenates results in series', function (done) { var arr = [0, 1, 2, 3]; Insync.concatSeries(arr, function (item, callback) { setTimeout(function () { callback(null, [item, item]); }, 100); }, function (err, results) { expect(err).to.not.exist(); expect(arr).to.deep.equal([0, 1, 2, 3]); expect(results).to.deep.equal([0, 0, 1, 1, 2, 2, 3, 3]); done(); }); }); }); describe('sortBy()', function () { it('performs an asynchronous sort', function (done) { var arr = [3, 1, 0, 2, 0]; Insync.sortBy(arr, function (item, callback) { setTimeout(function () { callback(null, item); }, 50); }, function (err, result) { expect(err).to.not.exist(); expect(arr).to.deep.equal([3, 1, 0, 2, 0]); expect(result).to.deep.equal([0, 0, 1, 2, 3]); done(); }); }); it('handles errors properly', function (done) { var arr = [3, 1, 0, 2, 0]; Insync.sortBy(arr, function (item, callback) { setTimeout(function () { if (item === 2) { return callback(new Error('foo')); } callback(null, item); }, 50); }, function (err, result) { expect(err).to.exist(); expect(arr).to.deep.equal([3, 1, 0, 2, 0]); expect(result).to.not.exist(); done(); }); }); }); }); describe('Flow', function () { describe('series()', function () { it('executes array of functions in series', function (done) { var callOrder = []; Insync.series([ function (callback) { setTimeout(function () { callOrder.push(0); callback(null, 0); }, 100); }, function (callback) { setTimeout(function () { callOrder.push(1); callback(null, 1); }, 100); }, function (callback) { setTimeout(function () { callOrder.push(2); callback(null, 2, 3); }, 10); } ], function (err, results) { expect(err).to.not.exist(); expect(callOrder).to.deep.equal([0, 1, 2]); expect(results).to.deep.equal([0, 1, [2, 3]]); done(); }); }); it('executes object of functions in series', function (done) { var callOrder = []; Insync.series({ zero: function (callback) { setTimeout(function () { callOrder.push(0); callback(null, 0); }, 100); }, one: function (callback) { setTimeout(function () { callOrder.push(1); callback(null, 1); }, 100); }, two: function (callback) { setTimeout(function () { callOrder.push(2); callback(null, 2, 3); }, 10); } }, function (err, results) { expect(err).to.not.exist(); expect(callOrder).to.deep.equal([0, 1, 2]); expect(results).to.deep.equal({ zero: 0, one: 1, two: [2, 3] }); done(); }); }); it('handles empty array of functions', function (done) { Insync.series([], function (err, results) { expect(err).to.not.exist(); expect(results).to.deep.equal([]); done(); }); }); it('handles array functions that call back with no arguments', function (done) { Insync.series([ function (callback) { setTimeout(function () { callback(); }, 100); } ], function (err, results) { expect(err).to.not.exist(); expect(results).to.deep.equal([undefined]); done(); }); }); it('handles object functions that call back with no arguments', function (done) { Insync.series({ zero: function (callback) { setTimeout(function () { callback(); }, 100); } }, function (err, results) { expect(err).to.not.exist(); expect(results).to.deep.equal({ zero: undefined }); done(); }); }); it('handles errors when an array is passed', function (done) { Insync.series([ function (callback) { setTimeout(function () { callback(new Error()); }, 100); } ], function (err, results) { expect(err).to.exist(); done(); }); }); it('handles errors when an object is passed', function (done) { Insync.series({ zero: function (callback) { setTimeout(function () { callback(new Error()); }, 100); } }, function (err, results) { expect(err).to.exist(); done(); }); }); it('handles case where no callback is provided', function (done) { var noop = Util.noop; Util.noop = function (error) { Util.noop = noop; expect(error).to.not.exist(); done(); }; Insync.series({ zero: function (callback) { setTimeout(function () { callback(null, 0); }, 100); } }); }); }); describe('parallel()', function () { it('executes array of functions in parallel', function (done) { var callOrder = []; Insync.parallel([ function (callback) { setTimeout(function () { callOrder.push(0); callback(null, 0); }, 50); }, function (callback) { setTimeout(function () { callOrder.push(1); callback(null, 1); }, 100); }, function (callback) { setTimeout(function () { callOrder.push(2); callback(null, 2, 3); }, 10); } ], function (err, results) { expect(err).to.not.exist(); expect(callOrder).to.deep.equal([2, 0, 1]); expect(results).to.deep.equal([0, 1, [2, 3]]); done(); }); }); it('executes object of functions in parallel', function (done) { var callOrder = []; Insync.parallel({ zero: function (callback) { setTimeout(function () { callOrder.push(0); callback(null, 0); }, 50); }, one: function (callback) { setTimeout(function () { callOrder.push(1); callback(null, 1); }, 100); }, two: function (callback) { setTimeout(function () { callOrder.push(2); callback(null, 2, 3); }, 10); } }, function (err, results) { expect(err).to.not.exist(); expect(callOrder).to.deep.equal([2, 0, 1]); expect(results).to.deep.equal({ zero: 0, one: 1, two: [2, 3] }); done(); }); }); it('handles case where no callback is provided', function (done) { var noop = Util.noop; Util.noop = function (error) { Util.noop = noop; expect(error).to.not.exist(); done(); }; Insync.parallel({ zero: function (callback) { setTimeout(function () { callback(null, 0); }, 100); } }); }); it('handles callbacks that pass no arguments', function (done) { var callOrder = []; Insync.parallel({ zero: function (callback) { setTimeout(function () { callOrder.push(0); callback(); }, 50); }, one: function (callback) { setTimeout(function () { callOrder.push(1); callback(); }, 100); }, two: function (callback) { setTimeout(function () { callOrder.push(2); callback(); }, 10); } }, function (err, results) { expect(err).to.not.exist(); expect(callOrder).to.deep.equal([2, 0, 1]); expect(results).to.deep.equal({ zero: undefined, one: undefined, two: undefined }); done(); }); }); }); describe('whilst()', function () { it('executes an asynchronous while loop', function (done) { var callOrder = []; var count = 0; Insync.whilst(function () { callOrder.push(['test', count]); return count < 5; }, function (callback) { callOrder.push(['iterator', count]); setTimeout(function () { count++; callback(); }, 10); }, function (err) { expect(err).to.not.exist(); expect(count).to.equal(5); expect(callOrder).to.deep.equal([ ['test', 0], ['iterator', 0], ['test', 1], ['iterator', 1], ['test', 2], ['iterator', 2], ['test', 3], ['iterator', 3], ['test', 4], ['iterator', 4], ['test', 5] ]); done(); }); }); it('handles errors in iterator', function (done) { var callOrder = []; var count = 0; Insync.whilst(function () { callOrder.push(['test', count]); return count < 5; }, function (callback) { callOrder.push(['iterator', count]); setTimeout(function () { count++; if (count === 2) { return callback(new Error('foo')); } callback(); }, 50); }, function (err) { expect(err).to.exist(); expect(count).to.equal(2); expect(callOrder).to.deep.equal([ ['test', 0], ['iterator', 0], ['test', 1], ['iterator', 1] ]); done(); }); }); }); describe('doWhilst()', function () { it('executes an asynchronous do...while loop', function (done) { var callOrder = []; var count = 0; Insync.doWhilst(function (callback) { callOrder.push(['iterator', count]); setTimeout(function () { count++; callback(); }, 10); }, function () { callOrder.push(['test', count]); return count < 5; }, function (err) { expect(err).to.not.exist(); expect(count).to.equal(5); expect(callOrder).to.deep.equal([ ['iterator', 0], ['test', 1], ['iterator', 1], ['test', 2], ['iterator', 2], ['test', 3], ['iterator', 3], ['test', 4], ['iterator', 4], ['test', 5] ]); done(); }); }); it('iterator calls back with arguments', function (done) { var callOrder = []; var count = 0; Insync.doWhilst(function (callback) { callOrder.push(['iterator', count]); setTimeout(function () { count++; callback(null, count); }, 10); }, function (cnt) { callOrder.push(['test', cnt]); return cnt < 5; }, function (err) { expect(err).to.not.exist(); expect(count).to.equal(5); expect(callOrder).to.deep.equal([ ['iterator', 0], ['test', 1], ['iterator', 1], ['test', 2], ['iterator', 2], ['test', 3], ['iterator', 3], ['test', 4], ['iterator', 4], ['test', 5] ]); done(); }); }); it('handles errors in iterator', function (done) { var callOrder = []; var count = 0; Insync.doWhilst(function (callback) { callOrder.push(['iterator', count]); setTimeout(function () { count++; if (count === 2) { return callback(new Error('foo')); } callback(); }, 50); }, function () { callOrder.push(['test', count]); return count < 5; }, function (err) { expect(err).to.exist(); expect(count).to.equal(2); expect(callOrder).to.deep.equal([ ['iterator', 0], ['test', 1], ['iterator', 1] ]); done(); }); }); }); describe('until()', function () { it('executes an asynchronous until loop', function (done) { var callOrder = []; var count = 0; Insync.until(function () { callOrder.push(['test', count]); return count === 5; }, function (callback) { callOrder.push(['iterator', count]); setTimeout(function () { count++; callback(); }, 10); }, function (err) { expect(err).to.not.exist(); expect(count).to.equal(5); expect(callOrder).to.deep.equal([ ['test', 0], ['iterator', 0], ['test', 1], ['iterator', 1], ['test', 2], ['iterator', 2],