async
Advanced tools
Comparing version 1.0.0 to 1.1.0
@@ -0,1 +1,25 @@ | ||
# v1.1.0 | ||
New Features: | ||
- `cargo` now supports all of the same methods and event callbacks as `queue`. | ||
- Added `ensureAsync` - A wrapper that ensures an async function calls its callback on a later tick. (#769) | ||
- Optimized `map`, `eachOf`, and `waterfall` families of functions | ||
- Passing a `null` or `undefined` array to `map`, `each`, `parallel` and families will be treated as an empty array (#667). | ||
- The callback is not optional for the composed results of `compose` and `seq`. (#618) | ||
- Reduced file size by 4kb, (minified version by 1kb) | ||
- Added code coverage through `nyc` and `coveralls` (#768) | ||
Bug Fixes: | ||
- `forever` will no longer stack overflow with a synchronous iterator (#622) | ||
- `eachLimit` and other limit functions will stop iterating once an error occurs (#754) | ||
- Always pass `null` in callbacks when there is no error (#439) | ||
- Ensure proper conditions when calling `drain()` after pushing an empty data set to a queue (#668) | ||
- `each` and family will properly handle an empty array (#578) | ||
- `eachSeries` and family will finish if the underlying array is modified during execution (#557) | ||
- `queue` will throw if a non-function is passed to `q.push()` (#593) | ||
- Doc fixes (#629, #766) | ||
# v1.0.0 | ||
@@ -2,0 +26,0 @@ |
931
lib/async.js
@@ -11,3 +11,3 @@ /*! | ||
var async = {}; | ||
var noop = function () {}; | ||
function noop() {} | ||
@@ -41,6 +41,15 @@ // global on the server, window in the browser | ||
called = true; | ||
fn.apply(root, arguments); | ||
fn.apply(this, arguments); | ||
}; | ||
} | ||
function _once(fn) { | ||
var called = false; | ||
return function() { | ||
if (called) return; | ||
called = true; | ||
fn.apply(this, arguments); | ||
}; | ||
} | ||
//// cross-browser compatiblity functions //// | ||
@@ -54,3 +63,18 @@ | ||
var _each = function (arr, iterator) { | ||
function _isArrayLike(arr) { | ||
return _isArray(arr) || ( | ||
// has a positive integer length property | ||
typeof arr.length === "number" && | ||
arr.length >= 0 && | ||
arr.length % 1 === 0 | ||
); | ||
} | ||
function _each(coll, iterator) { | ||
return _isArrayLike(coll) ? | ||
_arrayEach(coll, iterator) : | ||
_forEachOf(coll, iterator); | ||
} | ||
function _arrayEach(arr, iterator) { | ||
var index = -1, | ||
@@ -62,5 +86,5 @@ length = arr.length; | ||
} | ||
}; | ||
} | ||
var _map = function (arr, iterator) { | ||
function _map(arr, iterator) { | ||
var index = -1, | ||
@@ -74,16 +98,16 @@ length = arr.length, | ||
return result; | ||
}; | ||
} | ||
var _reduce = function (arr, iterator, memo) { | ||
_each(arr, function (x, i, a) { | ||
function _reduce(arr, iterator, memo) { | ||
_arrayEach(arr, function (x, i, a) { | ||
memo = iterator(memo, x, i, a); | ||
}); | ||
return memo; | ||
}; | ||
} | ||
var _forEachOf = function (object, iterator) { | ||
_each(_keys(object), function (key) { | ||
function _forEachOf(object, iterator) { | ||
_arrayEach(_keys(object), function (key) { | ||
iterator(object[key], key); | ||
}); | ||
}; | ||
} | ||
@@ -100,3 +124,23 @@ var _keys = Object.keys || function (obj) { | ||
var _baseSlice = function (arr, start) { | ||
function _keyIterator(coll) { | ||
var i = -1; | ||
var len; | ||
var keys; | ||
if (_isArrayLike(coll)) { | ||
len = coll.length; | ||
return function next() { | ||
i++; | ||
return i < len ? i : null; | ||
}; | ||
} else { | ||
keys = _keys(coll); | ||
len = keys.length; | ||
return function next() { | ||
i++; | ||
return i < len ? keys[i] : null; | ||
}; | ||
} | ||
} | ||
function _baseSlice(arr, start) { | ||
start = start || 0; | ||
@@ -116,4 +160,10 @@ var index = -1; | ||
return result; | ||
}; | ||
} | ||
function _withoutIndex(iterator) { | ||
return function (value, index, callback) { | ||
return iterator(value, callback); | ||
}; | ||
} | ||
//// exported async module functions //// | ||
@@ -157,152 +207,64 @@ | ||
async.forEach = | ||
async.each = function (arr, iterator, callback) { | ||
callback = callback || noop; | ||
if (!arr.length) { | ||
return callback(); | ||
} | ||
var completed = 0; | ||
_each(arr, function (x) { | ||
iterator(x, only_once(done) ); | ||
}); | ||
function done(err) { | ||
if (err) { | ||
callback(err); | ||
callback = noop; | ||
} | ||
else { | ||
completed += 1; | ||
if (completed >= arr.length) { | ||
callback(); | ||
} | ||
} | ||
} | ||
return async.eachOf(arr, _withoutIndex(iterator), callback); | ||
}; | ||
async.forEach = async.each; | ||
async.forEachSeries = | ||
async.eachSeries = function (arr, iterator, callback) { | ||
callback = callback || noop; | ||
if (!arr.length) { | ||
return callback(); | ||
} | ||
var completed = 0; | ||
var iterate = function () { | ||
iterator(arr[completed], function (err) { | ||
if (err) { | ||
callback(err); | ||
callback = noop; | ||
} | ||
else { | ||
completed += 1; | ||
if (completed >= arr.length) { | ||
callback(); | ||
} | ||
else { | ||
iterate(); | ||
} | ||
} | ||
}); | ||
}; | ||
iterate(); | ||
return async.eachOfSeries(arr, _withoutIndex(iterator), callback); | ||
}; | ||
async.forEachSeries = async.eachSeries; | ||
async.forEachLimit = | ||
async.eachLimit = function (arr, limit, iterator, callback) { | ||
var fn = _eachLimit(limit); | ||
fn.apply(null, [arr, iterator, callback]); | ||
return _eachOfLimit(limit)(arr, _withoutIndex(iterator), callback); | ||
}; | ||
async.forEachLimit = async.eachLimit; | ||
var _eachLimit = function (limit) { | ||
return function (arr, iterator, callback) { | ||
callback = callback || noop; | ||
if (!arr.length || limit <= 0) { | ||
return callback(); | ||
} | ||
var completed = 0; | ||
var started = 0; | ||
var running = 0; | ||
(function replenish () { | ||
if (completed >= arr.length) { | ||
return callback(); | ||
} | ||
while (running < limit && started < arr.length) { | ||
started += 1; | ||
running += 1; | ||
iterator(arr[started - 1], function (err) { | ||
if (err) { | ||
callback(err); | ||
callback = noop; | ||
} | ||
else { | ||
completed += 1; | ||
running -= 1; | ||
if (completed >= arr.length) { | ||
callback(); | ||
} | ||
else { | ||
replenish(); | ||
} | ||
} | ||
}); | ||
} | ||
})(); | ||
}; | ||
}; | ||
async.forEachOf = async.eachOf = function (object, iterator, callback) { | ||
callback = callback || function () {}; | ||
async.forEachOf = | ||
async.eachOf = function (object, iterator, callback) { | ||
callback = _once(callback || noop); | ||
object = object || []; | ||
var size = object.length || _keys(object).length; | ||
var completed = 0; | ||
if (!size) { | ||
return callback(); | ||
return callback(null); | ||
} | ||
_forEachOf(object, function (value, key) { | ||
iterator(object[key], key, function (err) { | ||
if (err) { | ||
callback(err); | ||
callback = function () {}; | ||
} else { | ||
completed += 1; | ||
if (completed === size) { | ||
callback(null); | ||
} | ||
} | ||
}); | ||
_each(object, function (value, key) { | ||
iterator(object[key], key, only_once(done)); | ||
}); | ||
function done(err) { | ||
if (err) { | ||
callback(err); | ||
} | ||
else { | ||
completed += 1; | ||
if (completed >= size) { | ||
callback(null); | ||
} | ||
} | ||
} | ||
}; | ||
async.forEachOfSeries = async.eachOfSeries = function (obj, iterator, callback) { | ||
callback = callback || function () {}; | ||
var keys = _keys(obj); | ||
var size = keys.length; | ||
if (!size) { | ||
return callback(); | ||
} | ||
var completed = 0; | ||
var iterate = function () { | ||
async.forEachOfSeries = | ||
async.eachOfSeries = function (obj, iterator, callback) { | ||
callback = _once(callback || noop); | ||
obj = obj || []; | ||
var nextKey = _keyIterator(obj); | ||
function iterate() { | ||
var sync = true; | ||
var key = keys[completed]; | ||
var key = nextKey(); | ||
if (key === null) { | ||
return callback(null); | ||
} | ||
iterator(obj[key], key, function (err) { | ||
if (err) { | ||
callback(err); | ||
callback = function () {}; | ||
} | ||
else { | ||
completed += 1; | ||
if (completed >= size) { | ||
callback(null); | ||
if (sync) { | ||
async.nextTick(iterate); | ||
} | ||
else { | ||
if (sync) { | ||
async.nextTick(iterate); | ||
} | ||
else { | ||
iterate(); | ||
} | ||
iterate(); | ||
} | ||
@@ -312,3 +274,3 @@ } | ||
sync = false; | ||
}; | ||
} | ||
iterate(); | ||
@@ -319,42 +281,43 @@ }; | ||
async.forEachOfLimit = async.eachOfLimit = function (obj, limit, iterator, callback) { | ||
_forEachOfLimit(limit)(obj, iterator, callback); | ||
async.forEachOfLimit = | ||
async.eachOfLimit = function (obj, limit, iterator, callback) { | ||
_eachOfLimit(limit)(obj, iterator, callback); | ||
}; | ||
var _forEachOfLimit = function (limit) { | ||
function _eachOfLimit(limit) { | ||
return function (obj, iterator, callback) { | ||
callback = callback || function () {}; | ||
var keys = _keys(obj); | ||
var size = keys.length; | ||
if (!size || limit <= 0) { | ||
return callback(); | ||
callback = _once(callback || noop); | ||
obj = obj || []; | ||
var nextKey = _keyIterator(obj); | ||
if (limit <= 0) { | ||
return callback(null); | ||
} | ||
var completed = 0; | ||
var started = 0; | ||
var done = false; | ||
var running = 0; | ||
var errored = false; | ||
(function replenish () { | ||
if (completed >= size) { | ||
return callback(); | ||
if (done && running <= 0) { | ||
return callback(null); | ||
} | ||
while (running < limit && started < size) { | ||
started += 1; | ||
while (running < limit && !errored) { | ||
var key = nextKey(); | ||
if (key === null) { | ||
done = true; | ||
if (running <= 0) { | ||
callback(null); | ||
} | ||
return; | ||
} | ||
running += 1; | ||
var key = keys[started - 1]; | ||
iterator(obj[key], key, function (err) { | ||
running -= 1; | ||
if (err) { | ||
callback(err); | ||
callback = function () {}; | ||
errored = true; | ||
} | ||
else { | ||
completed += 1; | ||
running -= 1; | ||
if (completed >= size) { | ||
callback(); | ||
} | ||
else { | ||
replenish(); | ||
} | ||
replenish(); | ||
} | ||
@@ -365,47 +328,34 @@ }); | ||
}; | ||
}; | ||
} | ||
var doParallel = function (fn) { | ||
return function () { | ||
var args = _baseSlice(arguments); | ||
return fn.apply(null, [async.each].concat(args)); | ||
function doParallel(fn) { | ||
return function (obj, iterator, callback) { | ||
return fn(async.eachOf, obj, iterator, callback); | ||
}; | ||
}; | ||
var doParallelLimit = function(limit, fn) { | ||
return function () { | ||
var args = _baseSlice(arguments); | ||
return fn.apply(null, [_eachLimit(limit)].concat(args)); | ||
} | ||
function doParallelLimit(limit, fn) { | ||
return function (obj, iterator, callback) { | ||
return fn(_eachOfLimit(limit), obj, iterator, callback); | ||
}; | ||
}; | ||
var doSeries = function (fn) { | ||
return function () { | ||
var args = _baseSlice(arguments); | ||
return fn.apply(null, [async.eachSeries].concat(args)); | ||
} | ||
function doSeries(fn) { | ||
return function (obj, iterator, callback) { | ||
return fn(async.eachOfSeries, obj, iterator, callback); | ||
}; | ||
}; | ||
} | ||
function _asyncMap(eachfn, arr, iterator, callback) { | ||
callback = _once(callback || noop); | ||
var results = []; | ||
eachfn(arr, function (value, index, callback) { | ||
iterator(value, function (err, v) { | ||
results[index] = v; | ||
callback(err); | ||
}); | ||
}, function (err) { | ||
callback(err, results); | ||
}); | ||
} | ||
var _asyncMap = function (eachfn, arr, iterator, callback) { | ||
arr = _map(arr, function (x, i) { | ||
return {index: i, value: x}; | ||
}); | ||
if (!callback) { | ||
eachfn(arr, function (x, callback) { | ||
iterator(x.value, function (err) { | ||
callback(err); | ||
}); | ||
}); | ||
} else { | ||
var results = []; | ||
eachfn(arr, function (x, callback) { | ||
iterator(x.value, function (err, v) { | ||
results[x.index] = v; | ||
callback(err); | ||
}); | ||
}, function (err) { | ||
callback(err, results); | ||
}); | ||
} | ||
}; | ||
async.map = doParallel(_asyncMap); | ||
@@ -417,10 +367,12 @@ async.mapSeries = doSeries(_asyncMap); | ||
var _mapLimit = function(limit) { | ||
function _mapLimit(limit) { | ||
return doParallelLimit(limit, _asyncMap); | ||
}; | ||
} | ||
// reduce only has a series version, as doing reduce in parallel won't | ||
// work in many situations. | ||
async.inject = | ||
async.foldl = | ||
async.reduce = function (arr, memo, iterator, callback) { | ||
async.eachSeries(arr, function (x, callback) { | ||
async.eachOfSeries(arr, function (x, i, callback) { | ||
iterator(memo, x, function (err, v) { | ||
@@ -431,10 +383,7 @@ memo = v; | ||
}, function (err) { | ||
callback(err, memo); | ||
callback(err || null, memo); | ||
}); | ||
}; | ||
// inject alias | ||
async.inject = async.reduce; | ||
// foldl alias | ||
async.foldl = async.reduce; | ||
async.foldr = | ||
async.reduceRight = function (arr, memo, iterator, callback) { | ||
@@ -446,6 +395,4 @@ var reversed = _map(arr, function (x) { | ||
}; | ||
// foldr alias | ||
async.foldr = async.reduceRight; | ||
var _filter = function (eachfn, arr, iterator, callback) { | ||
function _filter(eachfn, arr, iterator, callback) { | ||
var results = []; | ||
@@ -455,3 +402,3 @@ arr = _map(arr, function (x, i) { | ||
}); | ||
eachfn(arr, function (x, callback) { | ||
eachfn(arr, function (x, index, callback) { | ||
iterator(x.value, function (v) { | ||
@@ -463,3 +410,3 @@ if (v) { | ||
}); | ||
}, function (err) { | ||
}, function () { | ||
callback(_map(results.sort(function (a, b) { | ||
@@ -471,10 +418,11 @@ return a.index - b.index; | ||
}); | ||
}; | ||
} | ||
async.select = | ||
async.filter = doParallel(_filter); | ||
async.selectSeries = | ||
async.filterSeries = doSeries(_filter); | ||
// select alias | ||
async.select = async.filter; | ||
async.selectSeries = async.filterSeries; | ||
var _reject = function (eachfn, arr, iterator, callback) { | ||
function _reject(eachfn, arr, iterator, callback) { | ||
var results = []; | ||
@@ -484,3 +432,3 @@ arr = _map(arr, function (x, i) { | ||
}); | ||
eachfn(arr, function (x, callback) { | ||
eachfn(arr, function (x, index, callback) { | ||
iterator(x.value, function (v) { | ||
@@ -492,3 +440,3 @@ if (!v) { | ||
}); | ||
}, function (err) { | ||
}, function () { | ||
callback(_map(results.sort(function (a, b) { | ||
@@ -500,8 +448,8 @@ return a.index - b.index; | ||
}); | ||
}; | ||
} | ||
async.reject = doParallel(_reject); | ||
async.rejectSeries = doSeries(_reject); | ||
var _detect = function (eachfn, arr, iterator, main_callback) { | ||
eachfn(arr, function (x, callback) { | ||
function _detect(eachfn, arr, iterator, main_callback) { | ||
eachfn(arr, function (x, index, callback) { | ||
iterator(x, function (result) { | ||
@@ -516,11 +464,12 @@ if (result) { | ||
}); | ||
}, function (err) { | ||
}, function () { | ||
main_callback(); | ||
}); | ||
}; | ||
} | ||
async.detect = doParallel(_detect); | ||
async.detectSeries = doSeries(_detect); | ||
async.any = | ||
async.some = function (arr, iterator, main_callback) { | ||
async.each(arr, function (x, callback) { | ||
async.eachOf(arr, function (x, _, callback) { | ||
iterator(x, function (v) { | ||
@@ -533,11 +482,10 @@ if (v) { | ||
}); | ||
}, function (err) { | ||
}, function () { | ||
main_callback(false); | ||
}); | ||
}; | ||
// any alias | ||
async.any = async.some; | ||
async.all = | ||
async.every = function (arr, iterator, main_callback) { | ||
async.each(arr, function (x, callback) { | ||
async.eachOf(arr, function (x, _, callback) { | ||
iterator(x, function (v) { | ||
@@ -550,8 +498,6 @@ if (!v) { | ||
}); | ||
}, function (err) { | ||
}, function () { | ||
main_callback(true); | ||
}); | ||
}; | ||
// all alias | ||
async.all = async.every; | ||
@@ -573,19 +519,21 @@ async.sortBy = function (arr, iterator, callback) { | ||
else { | ||
var fn = function (left, right) { | ||
var a = left.criteria, b = right.criteria; | ||
return a < b ? -1 : a > b ? 1 : 0; | ||
}; | ||
callback(null, _map(results.sort(fn), function (x) { | ||
callback(null, _map(results.sort(comparator), function (x) { | ||
return x.value; | ||
})); | ||
} | ||
}); | ||
function comparator(left, right) { | ||
var a = left.criteria, b = right.criteria; | ||
return a < b ? -1 : a > b ? 1 : 0; | ||
} | ||
}; | ||
async.auto = function (tasks, callback) { | ||
callback = callback || noop; | ||
callback = _once(callback || noop); | ||
var keys = _keys(tasks); | ||
var remainingTasks = keys.length; | ||
if (!remainingTasks) { | ||
return callback(); | ||
return callback(null); | ||
} | ||
@@ -596,6 +544,6 @@ | ||
var listeners = []; | ||
var addListener = function (fn) { | ||
function addListener(fn) { | ||
listeners.unshift(fn); | ||
}; | ||
var removeListener = function (fn) { | ||
} | ||
function removeListener(fn) { | ||
for (var i = 0; i < listeners.length; i += 1) { | ||
@@ -607,23 +555,19 @@ if (listeners[i] === fn) { | ||
} | ||
}; | ||
var taskComplete = function () { | ||
} | ||
function taskComplete() { | ||
remainingTasks--; | ||
_each(listeners.slice(0), function (fn) { | ||
_arrayEach(listeners.slice(0), function (fn) { | ||
fn(); | ||
}); | ||
}; | ||
} | ||
addListener(function () { | ||
if (!remainingTasks) { | ||
var theCallback = callback; | ||
// prevent final callback from calling itself if it errors | ||
callback = noop; | ||
theCallback(null, results); | ||
callback(null, results); | ||
} | ||
}); | ||
_each(keys, function (k) { | ||
_arrayEach(keys, function (k) { | ||
var task = _isArray(tasks[k]) ? tasks[k]: [tasks[k]]; | ||
var taskCallback = function (err) { | ||
function taskCallback(err) { | ||
var args = _baseSlice(arguments, 1); | ||
@@ -635,3 +579,3 @@ if (args.length <= 1) { | ||
var safeResults = {}; | ||
_each(_keys(results), function(rkey) { | ||
_arrayEach(_keys(results), function(rkey) { | ||
safeResults[rkey] = results[rkey]; | ||
@@ -641,4 +585,2 @@ }); | ||
callback(err, safeResults); | ||
// stop subsequent errors hitting callback multiple times | ||
callback = noop; | ||
} | ||
@@ -649,3 +591,3 @@ else { | ||
} | ||
}; | ||
} | ||
var requires = task.slice(0, Math.abs(task.length - 1)) || []; | ||
@@ -663,7 +605,7 @@ // prevent dead-locks | ||
} | ||
var ready = function () { | ||
function ready() { | ||
return _reduce(requires, function (a, x) { | ||
return (a && results.hasOwnProperty(x)); | ||
}, true) && !results.hasOwnProperty(k); | ||
}; | ||
} | ||
if (ready()) { | ||
@@ -673,10 +615,10 @@ task[task.length - 1](taskCallback, results); | ||
else { | ||
var listener = function () { | ||
if (ready()) { | ||
removeListener(listener); | ||
task[task.length - 1](taskCallback, results); | ||
} | ||
}; | ||
addListener(listener); | ||
} | ||
function listener() { | ||
if (ready()) { | ||
removeListener(listener); | ||
task[task.length - 1](taskCallback, results); | ||
} | ||
} | ||
}); | ||
@@ -696,4 +638,5 @@ }; | ||
times = parseInt(times, 10) || DEFAULT_TIMES; | ||
var wrappedTask = function(wrappedCallback, wrappedResults) { | ||
var retryAttempt = function(task, finalAttempt) { | ||
function wrappedTask(wrappedCallback, wrappedResults) { | ||
function retryAttempt(task, finalAttempt) { | ||
return function(seriesCallback) { | ||
@@ -704,3 +647,4 @@ task(function(err, result){ | ||
}; | ||
}; | ||
} | ||
while (times) { | ||
@@ -713,3 +657,4 @@ attempts.push(retryAttempt(task, !(times-=1))); | ||
}); | ||
}; | ||
} | ||
// If a callback is passed, run this as a controll flow | ||
@@ -720,3 +665,3 @@ return callback ? wrappedTask() : wrappedTask; | ||
async.waterfall = function (tasks, callback) { | ||
callback = callback || noop; | ||
callback = _once(callback || noop); | ||
if (!_isArray(tasks)) { | ||
@@ -729,7 +674,6 @@ var err = new Error('First argument to waterfall must be an array of functions'); | ||
} | ||
var wrapIterator = function (iterator) { | ||
function wrapIterator(iterator) { | ||
return function (err) { | ||
if (err) { | ||
callback.apply(null, arguments); | ||
callback = noop; | ||
} | ||
@@ -745,49 +689,33 @@ else { | ||
} | ||
async.setImmediate(function () { | ||
iterator.apply(null, args); | ||
}); | ||
ensureAsync(iterator).apply(null, args); | ||
} | ||
}; | ||
}; | ||
} | ||
wrapIterator(async.iterator(tasks))(); | ||
}; | ||
var _parallel = function(eachfn, tasks, callback) { | ||
function _parallel(eachfn, tasks, callback) { | ||
callback = callback || noop; | ||
if (_isArray(tasks)) { | ||
eachfn.map(tasks, function (fn, callback) { | ||
if (fn) { | ||
fn(function (err) { | ||
var args = _baseSlice(arguments, 1); | ||
if (args.length <= 1) { | ||
args = args[0]; | ||
} | ||
callback.call(null, err, args); | ||
}); | ||
var results = _isArrayLike(tasks) ? [] : {}; | ||
eachfn(tasks, function (task, key, callback) { | ||
task(function (err) { | ||
var args = _baseSlice(arguments, 1); | ||
if (args.length <= 1) { | ||
args = args[0]; | ||
} | ||
}, callback); | ||
} | ||
else { | ||
var results = {}; | ||
eachfn.each(_keys(tasks), function (k, callback) { | ||
tasks[k](function (err) { | ||
var args = _baseSlice(arguments, 1); | ||
if (args.length <= 1) { | ||
args = args[0]; | ||
} | ||
results[k] = args; | ||
callback(err); | ||
}); | ||
}, function (err) { | ||
callback(err, results); | ||
results[key] = args; | ||
callback(err); | ||
}); | ||
} | ||
}; | ||
}, function (err) { | ||
callback(err, results); | ||
}); | ||
} | ||
async.parallel = function (tasks, callback) { | ||
_parallel({ map: async.map, each: async.each }, tasks, callback); | ||
_parallel(async.eachOf, tasks, callback); | ||
}; | ||
async.parallelLimit = function(tasks, limit, callback) { | ||
_parallel({ map: _mapLimit(limit), each: _eachLimit(limit) }, tasks, callback); | ||
_parallel(_eachOfLimit(limit), tasks, callback); | ||
}; | ||
@@ -797,35 +725,21 @@ | ||
callback = callback || noop; | ||
if (_isArray(tasks)) { | ||
async.mapSeries(tasks, function (fn, callback) { | ||
if (fn) { | ||
fn(function (err) { | ||
var args = _baseSlice(arguments, 1); | ||
if (args.length <= 1) { | ||
args = args[0]; | ||
} | ||
callback.call(null, err, args); | ||
}); | ||
var results = _isArrayLike(tasks) ? [] : {}; | ||
async.eachOfSeries(tasks, function (task, key, callback) { | ||
task(function (err) { | ||
var args = _baseSlice(arguments, 1); | ||
if (args.length <= 1) { | ||
args = args[0]; | ||
} | ||
}, callback); | ||
} | ||
else { | ||
var results = {}; | ||
async.eachSeries(_keys(tasks), function (k, callback) { | ||
tasks[k](function (err) { | ||
var args = _baseSlice(arguments, 1); | ||
if (args.length <= 1) { | ||
args = args[0]; | ||
} | ||
results[k] = args; | ||
callback(err); | ||
}); | ||
}, function (err) { | ||
callback(err, results); | ||
results[key] = args; | ||
callback(err); | ||
}); | ||
} | ||
}, function (err) { | ||
callback(err, results); | ||
}); | ||
}; | ||
async.iterator = function (tasks) { | ||
var makeCallback = function (index) { | ||
var fn = function () { | ||
function makeCallback(index) { | ||
function fn() { | ||
if (tasks.length) { | ||
@@ -835,3 +749,3 @@ tasks[index].apply(null, arguments); | ||
return fn.next(); | ||
}; | ||
} | ||
fn.next = function () { | ||
@@ -841,3 +755,3 @@ return (index < tasks.length - 1) ? makeCallback(index + 1): null; | ||
return fn; | ||
}; | ||
} | ||
return makeCallback(0); | ||
@@ -855,13 +769,13 @@ }; | ||
var _concat = function (eachfn, arr, fn, callback) { | ||
var r = []; | ||
eachfn(arr, function (x, cb) { | ||
function _concat(eachfn, arr, fn, callback) { | ||
var result = []; | ||
eachfn(arr, function (x, index, cb) { | ||
fn(x, function (err, y) { | ||
r = r.concat(y || []); | ||
result = result.concat(y || []); | ||
cb(err); | ||
}); | ||
}, function (err) { | ||
callback(err, r); | ||
callback(err, result); | ||
}); | ||
}; | ||
} | ||
async.concat = doParallel(_concat); | ||
@@ -880,3 +794,3 @@ async.concatSeries = doSeries(_concat); | ||
else { | ||
callback(); | ||
callback(null); | ||
} | ||
@@ -895,3 +809,3 @@ }; | ||
else { | ||
callback(); | ||
callback(null); | ||
} | ||
@@ -911,3 +825,3 @@ }); | ||
else { | ||
callback(); | ||
callback(null); | ||
} | ||
@@ -926,3 +840,3 @@ }; | ||
else { | ||
callback(); | ||
callback(null); | ||
} | ||
@@ -932,4 +846,4 @@ }); | ||
async.queue = function (worker, concurrency) { | ||
if (concurrency === undefined) { | ||
function _queue(worker, concurrency, payload) { | ||
if (concurrency == null) { | ||
concurrency = 1; | ||
@@ -941,33 +855,32 @@ } | ||
function _insert(q, data, pos, callback) { | ||
if (!q.started){ | ||
if (callback != null && typeof callback !== "function") { | ||
throw new Error("task callback must be a function"); | ||
} | ||
q.started = true; | ||
} | ||
if (!_isArray(data)) { | ||
data = [data]; | ||
} | ||
if(data.length === 0) { | ||
// call drain immediately if there are no tasks | ||
return async.setImmediate(function() { | ||
if (q.drain) { | ||
q.drain(); | ||
} | ||
}); | ||
} | ||
_each(data, function(task) { | ||
var item = { | ||
data: task, | ||
callback: typeof callback === 'function' ? callback : null | ||
}; | ||
if (!_isArray(data)) { | ||
data = [data]; | ||
} | ||
if(data.length === 0 && q.idle()) { | ||
// call drain immediately if there are no tasks | ||
return async.setImmediate(function() { | ||
q.drain(); | ||
}); | ||
} | ||
_arrayEach(data, function(task) { | ||
var item = { | ||
data: task, | ||
callback: callback || noop | ||
}; | ||
if (pos) { | ||
q.tasks.unshift(item); | ||
} else { | ||
q.tasks.push(item); | ||
} | ||
if (pos) { | ||
q.tasks.unshift(item); | ||
} else { | ||
q.tasks.push(item); | ||
} | ||
if (q.saturated && q.tasks.length === q.concurrency) { | ||
q.saturated(); | ||
} | ||
async.setImmediate(q.process); | ||
}); | ||
if (q.tasks.length === q.concurrency) { | ||
q.saturated(); | ||
} | ||
async.setImmediate(q.process); | ||
}); | ||
} | ||
@@ -979,37 +892,46 @@ | ||
concurrency: concurrency, | ||
saturated: null, | ||
empty: null, | ||
drain: null, | ||
saturated: noop, | ||
empty: noop, | ||
drain: noop, | ||
started: false, | ||
paused: false, | ||
push: function (data, callback) { | ||
_insert(q, data, false, callback); | ||
_insert(q, data, false, callback); | ||
}, | ||
kill: function () { | ||
q.drain = null; | ||
q.tasks = []; | ||
q.drain = noop; | ||
q.tasks = []; | ||
}, | ||
unshift: function (data, callback) { | ||
_insert(q, data, true, callback); | ||
_insert(q, data, true, callback); | ||
}, | ||
process: function () { | ||
if (!q.paused && workers < q.concurrency && q.tasks.length) { | ||
var task = q.tasks.shift(); | ||
if (q.empty && q.tasks.length === 0) { | ||
var tasks = payload ? | ||
q.tasks.splice(0, payload) : | ||
q.tasks.splice(0, q.tasks.length); | ||
var data = _map(tasks, function (task) { | ||
return task.data; | ||
}); | ||
if (q.tasks.length === 0) { | ||
q.empty(); | ||
} | ||
workers += 1; | ||
var next = function () { | ||
workers -= 1; | ||
if (task.callback) { | ||
task.callback.apply(task, arguments); | ||
} | ||
if (q.drain && q.tasks.length + workers === 0) { | ||
q.drain(); | ||
} | ||
q.process(); | ||
}; | ||
var cb = only_once(next); | ||
worker(task.data, cb); | ||
worker(data, cb); | ||
} | ||
function next() { | ||
workers -= 1; | ||
var args = arguments; | ||
_arrayEach(tasks, function (task) { | ||
task.callback.apply(task, args); | ||
}); | ||
if (q.tasks.length + workers === 0) { | ||
q.drain(); | ||
} | ||
q.process(); | ||
} | ||
}, | ||
@@ -1026,3 +948,2 @@ length: function () { | ||
pause: function () { | ||
if (q.paused === true) { return; } | ||
q.paused = true; | ||
@@ -1042,2 +963,10 @@ }, | ||
return q; | ||
} | ||
async.queue = function (worker, concurrency) { | ||
var q = _queue(function (items, cb) { | ||
worker(items[0], cb); | ||
}, concurrency, 1); | ||
return q; | ||
}; | ||
@@ -1048,3 +977,3 @@ | ||
function _compareTasks(a, b){ | ||
return a.priority - b.priority; | ||
return a.priority - b.priority; | ||
} | ||
@@ -1056,8 +985,8 @@ | ||
while (beg < end) { | ||
var mid = beg + ((end - beg + 1) >>> 1); | ||
if (compare(item, sequence[mid]) >= 0) { | ||
beg = mid; | ||
} else { | ||
end = mid - 1; | ||
} | ||
var mid = beg + ((end - beg + 1) >>> 1); | ||
if (compare(item, sequence[mid]) >= 0) { | ||
beg = mid; | ||
} else { | ||
end = mid - 1; | ||
} | ||
} | ||
@@ -1068,30 +997,29 @@ return beg; | ||
function _insert(q, data, priority, callback) { | ||
if (!q.started){ | ||
if (callback != null && typeof callback !== "function") { | ||
throw new Error("task callback must be a function"); | ||
} | ||
q.started = true; | ||
} | ||
if (!_isArray(data)) { | ||
data = [data]; | ||
} | ||
if(data.length === 0) { | ||
// call drain immediately if there are no tasks | ||
return async.setImmediate(function() { | ||
if (q.drain) { | ||
q.drain(); | ||
} | ||
}); | ||
} | ||
_each(data, function(task) { | ||
var item = { | ||
data: task, | ||
priority: priority, | ||
callback: typeof callback === 'function' ? callback : null | ||
}; | ||
if (!_isArray(data)) { | ||
data = [data]; | ||
} | ||
if(data.length === 0) { | ||
// call drain immediately if there are no tasks | ||
return async.setImmediate(function() { | ||
q.drain(); | ||
}); | ||
} | ||
_arrayEach(data, function(task) { | ||
var item = { | ||
data: task, | ||
priority: priority, | ||
callback: typeof callback === 'function' ? callback : noop | ||
}; | ||
q.tasks.splice(_binarySearch(q.tasks, item, _compareTasks) + 1, 0, item); | ||
q.tasks.splice(_binarySearch(q.tasks, item, _compareTasks) + 1, 0, item); | ||
if (q.saturated && q.tasks.length === q.concurrency) { | ||
q.saturated(); | ||
} | ||
async.setImmediate(q.process); | ||
}); | ||
if (q.tasks.length === q.concurrency) { | ||
q.saturated(); | ||
} | ||
async.setImmediate(q.process); | ||
}); | ||
} | ||
@@ -1104,3 +1032,3 @@ | ||
q.push = function (data, priority, callback) { | ||
_insert(q, data, priority, callback); | ||
_insert(q, data, priority, callback); | ||
}; | ||
@@ -1115,70 +1043,6 @@ | ||
async.cargo = function (worker, payload) { | ||
var working = false, | ||
tasks = []; | ||
var cargo = { | ||
tasks: tasks, | ||
payload: payload, | ||
saturated: null, | ||
empty: null, | ||
drain: null, | ||
drained: true, | ||
push: function (data, callback) { | ||
if (!_isArray(data)) { | ||
data = [data]; | ||
} | ||
_each(data, function(task) { | ||
tasks.push({ | ||
data: task, | ||
callback: typeof callback === 'function' ? callback : null | ||
}); | ||
cargo.drained = false; | ||
if (cargo.saturated && tasks.length === payload) { | ||
cargo.saturated(); | ||
} | ||
}); | ||
async.setImmediate(cargo.process); | ||
}, | ||
process: function process() { | ||
if (working) return; | ||
if (tasks.length === 0) { | ||
if(cargo.drain && !cargo.drained) cargo.drain(); | ||
cargo.drained = true; | ||
return; | ||
} | ||
var ts = typeof payload === 'number' ? | ||
tasks.splice(0, payload) : | ||
tasks.splice(0, tasks.length); | ||
var ds = _map(ts, function (task) { | ||
return task.data; | ||
}); | ||
if(cargo.empty) cargo.empty(); | ||
working = true; | ||
worker(ds, function () { | ||
working = false; | ||
var args = arguments; | ||
_each(ts, function (data) { | ||
if (data.callback) { | ||
data.callback.apply(null, args); | ||
} | ||
}); | ||
process(); | ||
}); | ||
}, | ||
length: function () { | ||
return tasks.length; | ||
}, | ||
running: function () { | ||
return working; | ||
} | ||
}; | ||
return cargo; | ||
return _queue(worker, 1, payload); | ||
}; | ||
var _console_fn = function (name) { | ||
function _console_fn(name) { | ||
return function (fn) { | ||
@@ -1195,3 +1059,3 @@ var args = _baseSlice(arguments, 1); | ||
else if (console[name]) { | ||
_each(args, function (x) { | ||
_arrayEach(args, function (x) { | ||
console[name](x); | ||
@@ -1203,3 +1067,3 @@ }); | ||
}; | ||
}; | ||
} | ||
async.log = _console_fn('log'); | ||
@@ -1217,3 +1081,3 @@ async.dir = _console_fn('dir'); | ||
}; | ||
var memoized = function () { | ||
function memoized() { | ||
var args = _baseSlice(arguments); | ||
@@ -1241,3 +1105,3 @@ var callback = args.pop(); | ||
} | ||
}; | ||
} | ||
memoized.memo = memo; | ||
@@ -1275,3 +1139,10 @@ memoized.unmemoized = fn; | ||
var args = _baseSlice(arguments); | ||
var callback = args.pop(); | ||
var callback = args.slice(-1)[0]; | ||
if (typeof callback == 'function') { | ||
args.pop(); | ||
} else { | ||
callback = noop; | ||
} | ||
async.reduce(fns, args, function (newargs, fn, cb) { | ||
@@ -1294,12 +1165,13 @@ fn.apply(that, newargs.concat([function () { | ||
var _applyEach = function (eachfn, fns /*args...*/) { | ||
var go = function () { | ||
function _applyEach(eachfn, fns /*args...*/) { | ||
function go() { | ||
var that = this; | ||
var args = _baseSlice(arguments); | ||
var callback = args.pop(); | ||
return eachfn(fns, function (fn, cb) { | ||
return eachfn(fns, function (fn, _, cb) { | ||
fn.apply(that, args.concat([cb])); | ||
}, | ||
callback); | ||
}; | ||
} | ||
if (arguments.length > 2) { | ||
@@ -1312,15 +1184,22 @@ var args = _baseSlice(arguments, 2); | ||
} | ||
} | ||
async.applyEach = function (/*fns, args...*/) { | ||
var args = _baseSlice(arguments); | ||
return _applyEach.apply(null, [async.eachOf].concat(args)); | ||
}; | ||
async.applyEach = doParallel(_applyEach); | ||
async.applyEachSeries = doSeries(_applyEach); | ||
async.applyEachSeries = function (/*fns, args...*/) { | ||
var args = _baseSlice(arguments); | ||
return _applyEach.apply(null, [async.eachOfSeries].concat(args)); | ||
}; | ||
async.forever = function (fn, callback) { | ||
var done = only_once(callback || noop); | ||
var task = ensureAsync(fn); | ||
function next(err) { | ||
if (err) { | ||
if (callback) { | ||
return callback(err); | ||
} | ||
throw err; | ||
return done(err); | ||
} | ||
fn(next); | ||
task(next); | ||
} | ||
@@ -1330,2 +1209,24 @@ next(); | ||
function ensureAsync(fn) { | ||
return function (/*...args, callback*/) { | ||
var args = _baseSlice(arguments); | ||
var callback = args.pop(); | ||
args.push(function () { | ||
var innerArgs = arguments; | ||
if (sync) { | ||
async.setImmediate(function () { | ||
callback.apply(null, innerArgs); | ||
}); | ||
} else { | ||
callback.apply(null, innerArgs); | ||
} | ||
}); | ||
var sync = true; | ||
fn.apply(this, args); | ||
sync = false; | ||
}; | ||
} | ||
async.ensureAsync = ensureAsync; | ||
// Node.js | ||
@@ -1332,0 +1233,0 @@ if (typeof module !== 'undefined' && module.exports) { |
@@ -6,3 +6,3 @@ { | ||
"author": "Caolan McMahon", | ||
"version": "1.0.0", | ||
"version": "1.1.0", | ||
"keywords": [ | ||
@@ -23,3 +23,4 @@ "async", | ||
"devDependencies": { | ||
"benchmark": "~1.0.0", | ||
"benchmark": "bestiejs/benchmark.js", | ||
"coveralls": "^2.11.2", | ||
"jshint": "~2.7.0", | ||
@@ -29,3 +30,5 @@ "lodash": ">=2.4.1", | ||
"nodeunit": ">0.0.0", | ||
"uglify-js": "1.2.x" | ||
"nyc": "^2.1.0", | ||
"uglify-js": "1.2.x", | ||
"yargs": "~3.9.1" | ||
}, | ||
@@ -45,3 +48,5 @@ "jam": { | ||
"test": "npm run-script lint && nodeunit test/test-async.js", | ||
"lint": "jshint lib/*.js test/*.js perf/*.js" | ||
"lint": "jshint lib/*.js test/*.js perf/*.js", | ||
"coverage": "nyc npm test && nyc report", | ||
"coveralls": "nyc npm test && nyc report --reporter=text-lcov | coveralls" | ||
}, | ||
@@ -61,2 +66,2 @@ "spm": { | ||
} | ||
} | ||
} |
323
README.md
@@ -5,4 +5,4 @@ # Async.js | ||
[![NPM version](http://img.shields.io/npm/v/async.svg)](https://www.npmjs.org/package/async) | ||
[![Coverage Status](https://coveralls.io/repos/caolan/async/badge.svg?branch=master)](https://coveralls.io/r/caolan/async?branch=master) | ||
Async is a utility module which provides straight-forward, powerful functions | ||
@@ -56,2 +56,39 @@ for working with asynchronous JavaScript. Although originally designed for | ||
<a name="stack-overflow"> | ||
### Synchronous iteration functions | ||
If you get an error like `RangeError: Maximum call stack size exceeded.` or other stack overflow issues when using async, you are likely using a synchronous iterator. By *synchronous* we mean a function that calls its callback on the same tick in the javascript event loop, without doing any I/O or using any timers. Calling many callbacks iteratively will quickly overflow the stack. If you run into this issue, just defer your callback with `async.nextTick` to start a new call stack on the next tick of the event loop. | ||
This can also arise by accident if you callback early in certain cases: | ||
```js | ||
async.eachSeries(hugeArray, function iterator(item, callback) { | ||
if (inCache(item)) { | ||
callback(null, cache[item]); // if many items are cached, you'll overflow | ||
} else { | ||
doSomeIO(item, callback); | ||
} | ||
}, function done() { | ||
//... | ||
}); | ||
``` | ||
Just change it to: | ||
```js | ||
async.eachSeries(hugeArray, function iterator(item, callback) { | ||
if (inCache(item)) { | ||
async.setImmediate(function () { | ||
callback(null, cache[item]); | ||
}); | ||
} else { | ||
doSomeIO(item, callback); | ||
//... | ||
``` | ||
Async guards against synchronous functions in some, but not all, cases. If you are still running into stack overflows, you can defer as suggested above, or wrap functions with [`async.ensureAsync`](#ensureAsync) Functions that are asynchronous by their nature do not have this problem and don't need the extra callback deferral. | ||
If javascript's event loop is still a bit nebulous, check out [this article](http://blog.carbonfive.com/2013/10/27/the-javascript-event-loop-explained/) or [this talk](http://2014.jsconf.eu/speakers/philip-roberts-what-the-heck-is-the-event-loop-anyway.html) for more detailed information about how it works. | ||
### Binding a context to an iterator | ||
@@ -67,3 +104,3 @@ | ||
squareExponent: 2, | ||
square: function(number, callback){ | ||
square: function(number, callback){ | ||
var result = Math.pow(number, this.squareExponent); | ||
@@ -86,3 +123,3 @@ setTimeout(function(){ | ||
// With the help of bind we can attach a context to the iterator before | ||
// passing it to async. Now the square function will be executed in its | ||
// passing it to async. Now the square function will be executed in its | ||
// 'home' AsyncSquaringLibrary context and the value of `this.squareExponent` | ||
@@ -101,3 +138,3 @@ // will be as expected. | ||
As well as using Bower: | ||
As well as using Bower: | ||
@@ -110,3 +147,3 @@ bower install async | ||
So far it's been tested in IE6, IE7, IE8, FF3.6 and Chrome 5. | ||
So far it's been tested in IE6, IE7, IE8, FF3.6 and Chrome 5. | ||
@@ -183,2 +220,3 @@ Usage: | ||
* [`unmemoize`](#unmemoize) | ||
* [`ensureAsync`](#ensureAsync) | ||
* [`log`](#log) | ||
@@ -193,3 +231,3 @@ * [`dir`](#dir) | ||
<a name="each" /> | ||
### each(arr, iterator, callback) | ||
### each(arr, iterator, [callback]) | ||
@@ -208,7 +246,7 @@ Applies the function `iterator` to each item in `arr`, in parallel. | ||
* `iterator(item, callback)` - A function to apply to each item in `arr`. | ||
The iterator is passed a `callback(err)` which must be called once it has | ||
completed. If no error has occurred, the `callback` should be run without | ||
The iterator is passed a `callback(err)` which must be called once it has | ||
completed. If no error has occurred, the `callback` should be run without | ||
arguments or with an explicit `null` argument. The array index is not passed | ||
to the iterator. If you need the index, use [`forEachOf`](#forEachOf). | ||
* `callback(err)` - A callback which is called when all `iterator` functions | ||
* `callback(err)` - *Optional* A callback which is called when all `iterator` functions | ||
have finished, or an error occurs. | ||
@@ -229,9 +267,9 @@ | ||
```js | ||
// assuming openFiles is an array of file names | ||
// assuming openFiles is an array of file names | ||
async.each(openFiles, function(file, callback) { | ||
// Perform operation on file here. | ||
console.log('Processing file ' + file); | ||
if( file.length > 32 ) { | ||
@@ -261,6 +299,6 @@ console.log('This file name is too long'); | ||
<a name="eachSeries" /> | ||
### eachSeries(arr, iterator, callback) | ||
### eachSeries(arr, iterator, [callback]) | ||
The same as [`each`](#each), only `iterator` is applied to each item in `arr` in | ||
series. The next `iterator` is only called once the current one has completed. | ||
series. The next `iterator` is only called once the current one has completed. | ||
This means the `iterator` functions will complete in order. | ||
@@ -273,8 +311,8 @@ | ||
<a name="eachLimit" /> | ||
### eachLimit(arr, limit, iterator, callback) | ||
### eachLimit(arr, limit, iterator, [callback]) | ||
The same as [`each`](#each), only no more than `limit` `iterator`s will be simultaneously | ||
The same as [`each`](#each), only no more than `limit` `iterator`s will be simultaneously | ||
running at any time. | ||
Note that the items in `arr` are not processed in batches, so there is no guarantee that | ||
Note that the items in `arr` are not processed in batches, so there is no guarantee that | ||
the first `limit` `iterator` functions will complete before any others are started. | ||
@@ -287,6 +325,6 @@ | ||
* `iterator(item, callback)` - A function to apply to each item in `arr`. | ||
The iterator is passed a `callback(err)` which must be called once it has | ||
completed. If no error has occurred, the callback should be run without | ||
The iterator is passed a `callback(err)` which must be called once it has | ||
completed. If no error has occurred, the callback should be run without | ||
arguments or with an explicit `null` argument. | ||
* `callback(err)` - A callback which is called when all `iterator` functions | ||
* `callback(err)` - *Optional* A callback which is called when all `iterator` functions | ||
have finished, or an error occurs. | ||
@@ -310,3 +348,3 @@ | ||
### forEachOf(obj, iterator, callback) | ||
### forEachOf(obj, iterator, [callback]) | ||
@@ -318,8 +356,8 @@ Like `each`, except that it iterates over objects, and passes the key as the second argument to the iterator. | ||
* `obj` - An object or array to iterate over. | ||
* `iterator(item, key, callback)` - A function to apply to each item in `obj`. | ||
The `key` is the item's key, or index in the case of an array. The iterator is | ||
* `iterator(item, key, callback)` - A function to apply to each item in `obj`. | ||
The `key` is the item's key, or index in the case of an array. The iterator is | ||
passed a `callback(err)` which must be called once it has completed. If no | ||
error has occurred, the callback should be run without arguments or with an | ||
explicit `null` argument. | ||
* `callback(err)` - A callback which is called when all `iterator` functions have finished, or an error occurs. | ||
* `callback(err)` - *Optional* A callback which is called when all `iterator` functions have finished, or an error occurs. | ||
@@ -354,3 +392,3 @@ __Example__ | ||
### forEachOfSeries(obj, iterator, callback) | ||
### forEachOfSeries(obj, iterator, [callback]) | ||
@@ -364,3 +402,3 @@ Like [`forEachOf`](#forEachOf), except only one `iterator` is run at a time. The order of execution is not guaranteed for objects, but it will be guaranteed for arrays. | ||
### forEachOfLimit(obj, limit, iterator, callback) | ||
### forEachOfLimit(obj, limit, iterator, [callback]) | ||
@@ -373,12 +411,12 @@ Like [`forEachOf`](#forEachOf), except the number of `iterator`s running at a given time is controlled by `limit`. | ||
<a name="map" /> | ||
### map(arr, iterator, callback) | ||
### map(arr, iterator, [callback]) | ||
Produces a new array of values by mapping each value in `arr` through | ||
the `iterator` function. The `iterator` is called with an item from `arr` and a | ||
callback for when it has finished processing. Each of these callback takes 2 arguments: | ||
an `error`, and the transformed item from `arr`. If `iterator` passes an error to its | ||
callback for when it has finished processing. Each of these callback takes 2 arguments: | ||
an `error`, and the transformed item from `arr`. If `iterator` passes an error to its | ||
callback, the main `callback` (for the `map` function) is immediately called with the error. | ||
Note, that since this function applies the `iterator` to each item in parallel, | ||
there is no guarantee that the `iterator` functions will complete in order. | ||
there is no guarantee that the `iterator` functions will complete in order. | ||
However, the results array will be in the same order as the original `arr`. | ||
@@ -390,5 +428,5 @@ | ||
* `iterator(item, callback)` - A function to apply to each item in `arr`. | ||
The iterator is passed a `callback(err, transformed)` which must be called once | ||
The iterator is passed a `callback(err, transformed)` which must be called once | ||
it has completed with an error (which can be `null`) and a transformed item. | ||
* `callback(err, results)` - A callback which is called when all `iterator` | ||
* `callback(err, results)` - *Optional* A callback which is called when all `iterator` | ||
functions have finished, or an error occurs. Results is an array of the | ||
@@ -408,6 +446,6 @@ transformed items from the `arr`. | ||
<a name="mapSeries" /> | ||
### mapSeries(arr, iterator, callback) | ||
### mapSeries(arr, iterator, [callback]) | ||
The same as [`map`](#map), only the `iterator` is applied to each item in `arr` in | ||
series. The next `iterator` is only called once the current one has completed. | ||
series. The next `iterator` is only called once the current one has completed. | ||
The results array will be in the same order as the original. | ||
@@ -419,8 +457,8 @@ | ||
<a name="mapLimit" /> | ||
### mapLimit(arr, limit, iterator, callback) | ||
### mapLimit(arr, limit, iterator, [callback]) | ||
The same as [`map`](#map), only no more than `limit` `iterator`s will be simultaneously | ||
The same as [`map`](#map), only no more than `limit` `iterator`s will be simultaneously | ||
running at any time. | ||
Note that the items are not processed in batches, so there is no guarantee that | ||
Note that the items are not processed in batches, so there is no guarantee that | ||
the first `limit` `iterator` functions will complete before any others are started. | ||
@@ -433,3 +471,3 @@ | ||
* `iterator(item, callback)` - A function to apply to each item in `arr`. | ||
The iterator is passed a `callback(err, transformed)` which must be called once | ||
The iterator is passed a `callback(err, transformed)` which must be called once | ||
it has completed with an error (which can be `null`) and a transformed item. | ||
@@ -452,3 +490,3 @@ * `callback(err, results)` - A callback which is called when all `iterator` | ||
<a name="filter" /> | ||
### filter(arr, iterator, callback) | ||
### filter(arr, iterator, [callback]) | ||
@@ -468,5 +506,5 @@ __Alias:__ `select` | ||
* `iterator(item, callback)` - A truth test to apply to each item in `arr`. | ||
The `iterator` is passed a `callback(truthValue)`, which must be called with a | ||
The `iterator` is passed a `callback(truthValue)`, which must be called with a | ||
boolean argument once it has completed. | ||
* `callback(results)` - A callback which is called after all the `iterator` | ||
* `callback(results)` - *Optional* A callback which is called after all the `iterator` | ||
functions have finished. | ||
@@ -486,3 +524,3 @@ | ||
<a name="filterSeries" /> | ||
### filterSeries(arr, iterator, callback) | ||
### filterSeries(arr, iterator, [callback]) | ||
@@ -492,3 +530,3 @@ __Alias:__ `selectSeries` | ||
The same as [`filter`](#filter) only the `iterator` is applied to each item in `arr` in | ||
series. The next `iterator` is only called once the current one has completed. | ||
series. The next `iterator` is only called once the current one has completed. | ||
The results array will be in the same order as the original. | ||
@@ -499,3 +537,3 @@ | ||
<a name="reject" /> | ||
### reject(arr, iterator, callback) | ||
### reject(arr, iterator, [callback]) | ||
@@ -507,3 +545,3 @@ The opposite of [`filter`](#filter). Removes values that pass an `async` truth test. | ||
<a name="rejectSeries" /> | ||
### rejectSeries(arr, iterator, callback) | ||
### rejectSeries(arr, iterator, [callback]) | ||
@@ -517,3 +555,3 @@ The same as [`reject`](#reject), only the `iterator` is applied to each item in `arr` | ||
<a name="reduce" /> | ||
### reduce(arr, memo, iterator, callback) | ||
### reduce(arr, memo, iterator, [callback]) | ||
@@ -523,8 +561,8 @@ __Aliases:__ `inject`, `foldl` | ||
Reduces `arr` into a single value using an async `iterator` to return | ||
each successive step. `memo` is the initial state of the reduction. | ||
This function only operates in series. | ||
each successive step. `memo` is the initial state of the reduction. | ||
This function only operates in series. | ||
For performance reasons, it may make sense to split a call to this function into | ||
a parallel map, and then use the normal `Array.prototype.reduce` on the results. | ||
This function is for situations where each step in the reduction needs to be async; | ||
For performance reasons, it may make sense to split a call to this function into | ||
a parallel map, and then use the normal `Array.prototype.reduce` on the results. | ||
This function is for situations where each step in the reduction needs to be async; | ||
if you can get the data before reducing it, then it's probably a good idea to do so. | ||
@@ -538,7 +576,7 @@ | ||
array to produce the next step in the reduction. The `iterator` is passed a | ||
`callback(err, reduction)` which accepts an optional error as its first | ||
argument, and the state of the reduction as the second. If an error is | ||
passed to the callback, the reduction is stopped and the main `callback` is | ||
`callback(err, reduction)` which accepts an optional error as its first | ||
argument, and the state of the reduction as the second. If an error is | ||
passed to the callback, the reduction is stopped and the main `callback` is | ||
immediately called with the error. | ||
* `callback(err, result)` - A callback which is called after all the `iterator` | ||
* `callback(err, result)` - *Optional* A callback which is called after all the `iterator` | ||
functions have finished. Result is the reduced value. | ||
@@ -562,3 +600,3 @@ | ||
<a name="reduceRight" /> | ||
### reduceRight(arr, memo, iterator, callback) | ||
### reduceRight(arr, memo, iterator, [callback]) | ||
@@ -573,3 +611,3 @@ __Alias:__ `foldr` | ||
<a name="detect" /> | ||
### detect(arr, iterator, callback) | ||
### detect(arr, iterator, [callback]) | ||
@@ -587,8 +625,8 @@ Returns the first value in `arr` that passes an async truth test. The | ||
* `iterator(item, callback)` - A truth test to apply to each item in `arr`. | ||
The iterator is passed a `callback(truthValue)` which must be called with a | ||
boolean argument once it has completed. | ||
* `callback(result)` - A callback which is called as soon as any iterator returns | ||
The iterator is passed a `callback(truthValue)` which must be called with a | ||
boolean argument once it has completed. **Note: this callback does not take an error as its first argument.** | ||
* `callback(result)` - *Optional* A callback which is called as soon as any iterator returns | ||
`true`, or after all the `iterator` functions have finished. Result will be | ||
the first item in the array that passes the truth test (iterator) or the | ||
value `undefined` if none passed. | ||
value `undefined` if none passed. **Note: this callback does not take an error as its first argument.** | ||
@@ -606,3 +644,3 @@ __Example__ | ||
<a name="detectSeries" /> | ||
### detectSeries(arr, iterator, callback) | ||
### detectSeries(arr, iterator, [callback]) | ||
@@ -617,3 +655,3 @@ The same as [`detect`](#detect), only the `iterator` is applied to each item in `arr` | ||
<a name="sortBy" /> | ||
### sortBy(arr, iterator, callback) | ||
### sortBy(arr, iterator, [callback]) | ||
@@ -629,3 +667,3 @@ Sorts a list by the results of running each `arr` value through an async `iterator`. | ||
criteria. | ||
* `callback(err, results)` - A callback which is called after all the `iterator` | ||
* `callback(err, results)` - *Optional* A callback which is called after all the `iterator` | ||
functions have finished, or an error occurs. Results is the items from | ||
@@ -670,3 +708,3 @@ the original `arr` sorted by the values returned by the `iterator` calls. | ||
<a name="some" /> | ||
### some(arr, iterator, callback) | ||
### some(arr, iterator, [callback]) | ||
@@ -685,8 +723,9 @@ __Alias:__ `any` | ||
* `iterator(item, callback)` - A truth test to apply to each item in the array | ||
in parallel. The iterator is passed a callback(truthValue) which must be | ||
in parallel. The iterator is passed a `callback(truthValue)`` which must be | ||
called with a boolean argument once it has completed. | ||
* `callback(result)` - A callback which is called as soon as any iterator returns | ||
* `callback(result)` - *Optional* A callback which is called as soon as any iterator returns | ||
`true`, or after all the iterator functions have finished. Result will be | ||
either `true` or `false` depending on the values of the async tests. | ||
**Note: the callbacks do not take an error as their first argument.** | ||
__Example__ | ||
@@ -703,3 +742,3 @@ | ||
<a name="every" /> | ||
### every(arr, iterator, callback) | ||
### every(arr, iterator, [callback]) | ||
@@ -717,8 +756,10 @@ __Alias:__ `all` | ||
* `iterator(item, callback)` - A truth test to apply to each item in the array | ||
in parallel. The iterator is passed a callback(truthValue) which must be | ||
in parallel. The iterator is passed a `callback(truthValue)` which must be | ||
called with a boolean argument once it has completed. | ||
* `callback(result)` - A callback which is called after all the `iterator` | ||
* `callback(result)` - *Optional* A callback which is called after all the `iterator` | ||
functions have finished. Result will be either `true` or `false` depending on | ||
the values of the async tests. | ||
**Note: the callbacks do not take an error as their first argument.** | ||
__Example__ | ||
@@ -735,3 +776,3 @@ | ||
<a name="concat" /> | ||
### concat(arr, iterator, callback) | ||
### concat(arr, iterator, [callback]) | ||
@@ -747,5 +788,5 @@ Applies `iterator` to each item in `arr`, concatenating the results. Returns the | ||
* `iterator(item, callback)` - A function to apply to each item in `arr`. | ||
The iterator is passed a `callback(err, results)` which must be called once it | ||
The iterator is passed a `callback(err, results)` which must be called once it | ||
has completed with an error (which can be `null`) and an array of results. | ||
* `callback(err, results)` - A callback which is called after all the `iterator` | ||
* `callback(err, results)` - *Optional* A callback which is called after all the `iterator` | ||
functions have finished, or an error occurs. Results is an array containing | ||
@@ -765,3 +806,3 @@ the concatenated results of the `iterator` function. | ||
<a name="concatSeries" /> | ||
### concatSeries(arr, iterator, callback) | ||
### concatSeries(arr, iterator, [callback]) | ||
@@ -778,3 +819,3 @@ Same as [`concat`](#concat), but executes in series instead of parallel. | ||
function has completed. If any functions in the series pass an error to its | ||
callback, no more functions are run, and `callback` is immediately called with the value of the error. | ||
callback, no more functions are run, and `callback` is immediately called with the value of the error. | ||
Otherwise, `callback` receives an array of results when `tasks` have completed. | ||
@@ -788,3 +829,3 @@ | ||
**Note** that while many implementations preserve the order of object properties, the | ||
[ECMAScript Language Specifcation](http://www.ecma-international.org/ecma-262/5.1/#sec-8.6) | ||
[ECMAScript Language Specifcation](http://www.ecma-international.org/ecma-262/5.1/#sec-8.6) | ||
explicitly states that | ||
@@ -795,3 +836,3 @@ | ||
So if you rely on the order in which your series of functions are executed, and want | ||
this to work on all platforms, consider using an array. | ||
this to work on all platforms, consider using an array. | ||
@@ -804,3 +845,3 @@ __Arguments__ | ||
* `callback(err, results)` - An optional callback to run once all the functions | ||
have completed. This function gets a results array (or object) containing all | ||
have completed. This function gets a results array (or object) containing all | ||
the result arguments passed to the `task` callbacks. | ||
@@ -864,7 +905,7 @@ | ||
* `tasks` - An array or object containing functions to run. Each function is passed | ||
a `callback(err, result)` which it must call on completion with an error `err` | ||
* `tasks` - An array or object containing functions to run. Each function is passed | ||
a `callback(err, result)` which it must call on completion with an error `err` | ||
(which can be `null`) and an optional `result` value. | ||
* `callback(err, results)` - An optional callback to run once all the functions | ||
have completed. This function gets a results array (or object) containing all | ||
have completed. This function gets a results array (or object) containing all | ||
the result arguments passed to the task callbacks. | ||
@@ -917,6 +958,6 @@ | ||
The same as [`parallel`](#parallel), only `tasks` are executed in parallel | ||
The same as [`parallel`](#parallel), only `tasks` are executed in parallel | ||
with a maximum of `limit` tasks executing at any time. | ||
Note that the `tasks` are not executed in batches, so there is no guarantee that | ||
Note that the `tasks` are not executed in batches, so there is no guarantee that | ||
the first `limit` tasks will complete before any others are started. | ||
@@ -926,3 +967,3 @@ | ||
* `tasks` - An array or object containing functions to run, each function is passed | ||
* `tasks` - An array or object containing functions to run, each function is passed | ||
a `callback(err, result)` it must call on completion with an error `err` (which can | ||
@@ -932,3 +973,3 @@ be `null`) and an optional `result` value. | ||
* `callback(err, results)` - An optional callback to run once all the functions | ||
have completed. This function gets a results array (or object) containing all | ||
have completed. This function gets a results array (or object) containing all | ||
the result arguments passed to the `task` callbacks. | ||
@@ -948,3 +989,3 @@ | ||
* `fn(callback)` - A function which is called each time `test` passes. The function is | ||
passed a `callback(err)`, which must be called once it has completed with an | ||
passed a `callback(err)`, which must be called once it has completed with an | ||
optional `err` argument. | ||
@@ -976,4 +1017,4 @@ * `callback(err)` - A callback which is called after the test fails and repeated | ||
The post-check version of [`whilst`](#whilst). To reflect the difference in | ||
the order of operations, the arguments `test` and `fn` are switched. | ||
The post-check version of [`whilst`](#whilst). To reflect the difference in | ||
the order of operations, the arguments `test` and `fn` are switched. | ||
@@ -1002,3 +1043,3 @@ `doWhilst` is to `whilst` as `do while` is to `while` in plain JavaScript. | ||
<a name="forever" /> | ||
### forever(fn, errback) | ||
### forever(fn, [errback]) | ||
@@ -1036,5 +1077,5 @@ Calls the asynchronous function `fn` with a callback parameter that allows it to | ||
* `tasks` - An array of functions to run, each function is passed a | ||
* `tasks` - An array of functions to run, each function is passed a | ||
`callback(err, result1, result2, ...)` it must call on completion. The first | ||
argument is an error (which can be `null`) and any further arguments will be | ||
argument is an error (which can be `null`) and any further arguments will be | ||
passed as arguments in order to the next task. | ||
@@ -1062,3 +1103,3 @@ * `callback(err, [results])` - An optional callback to run once all the functions | ||
], function (err, result) { | ||
// result now equals 'done' | ||
// result now equals 'done' | ||
}); | ||
@@ -1125,3 +1166,3 @@ ``` | ||
// Part of an app, that fetches cats of the logged user. | ||
// This example uses `seq` function to avoid overnesting and error | ||
// This example uses `seq` function to avoid overnesting and error | ||
// handling clutter. | ||
@@ -1150,3 +1191,3 @@ app.get('/cats', function(request, response) { | ||
Applies the provided arguments to each function in the array, calling | ||
Applies the provided arguments to each function in the array, calling | ||
`callback` after all functions have completed. If you only provide the first | ||
@@ -1187,7 +1228,7 @@ argument, then it will return a function which lets you pass in the | ||
<a name="queue" /> | ||
### queue(worker, concurrency) | ||
### queue(worker, [concurrency]) | ||
Creates a `queue` object with the specified `concurrency`. Tasks added to the | ||
`queue` are processed in parallel (up to the `concurrency` limit). If all | ||
`worker`s are in progress, the task is queued until one becomes available. | ||
`worker`s are in progress, the task is queued until one becomes available. | ||
Once a `worker` completes a `task`, that `task`'s callback is called. | ||
@@ -1198,6 +1239,6 @@ | ||
* `worker(task, callback)` - An asynchronous function for processing a queued | ||
task, which must call its `callback(err)` argument when finished, with an | ||
optional `error` as an argument. | ||
task, which must call its `callback(err)` argument when finished, with an | ||
optional `error` as an argument. If you want to handle errors from an individual task, pass a callback to `q.push()`. | ||
* `concurrency` - An `integer` for determining how many `worker` functions should be | ||
run in parallel. | ||
run in parallel. If omitted, the concurrency defaults to `1`. If the concurrency is `0`, an error is thrown. | ||
@@ -1216,7 +1257,7 @@ __Queue objects__ | ||
alter the concurrency on-the-fly. | ||
* `push(task, [callback])` - add a new task to the `queue`. Calls `callback` once | ||
* `push(task, [callback])` - add a new task to the `queue`. Calls `callback` once | ||
the `worker` has finished processing the task. Instead of a single task, a `tasks` array | ||
can be submitted. The respective callback is used for every task in the list. | ||
* `unshift(task, [callback])` - add a new task to the front of the `queue`. | ||
* `saturated` - a callback that is called when the `queue` length hits the `concurrency` limit, | ||
* `saturated` - a callback that is called when the `queue` length hits the `concurrency` limit, | ||
and further tasks will be queued. | ||
@@ -1289,3 +1330,3 @@ * `empty` - a callback that is called when the last item from the `queue` is given to a `worker`. | ||
the `worker` has completed some tasks, each callback of those tasks is called. | ||
Check out [this animation](https://camo.githubusercontent.com/6bbd36f4cf5b35a0f11a96dcd2e97711ffc2fb37/68747470733a2f2f662e636c6f75642e6769746875622e636f6d2f6173736574732f313637363837312f36383130382f62626330636662302d356632392d313165322d393734662d3333393763363464633835382e676966) for how `cargo` and `queue` work. | ||
Check out [these](https://camo.githubusercontent.com/6bbd36f4cf5b35a0f11a96dcd2e97711ffc2fb37/68747470733a2f2f662e636c6f75642e6769746875622e636f6d2f6173736574732f313637363837312f36383130382f62626330636662302d356632392d313165322d393734662d3333393763363464633835382e676966) [animations](https://camo.githubusercontent.com/f4810e00e1c5f5f8addbe3e9f49064fd5d102699/68747470733a2f2f662e636c6f75642e6769746875622e636f6d2f6173736574732f313637363837312f36383130312f38346339323036362d356632392d313165322d383134662d3964336430323431336266642e676966) for how `cargo` and `queue` work. | ||
@@ -1299,3 +1340,3 @@ While [queue](#queue) passes only one task to one of a group of workers | ||
* `worker(tasks, callback)` - An asynchronous function for processing an array of | ||
queued tasks, which must call its `callback(err)` argument when finished, with | ||
queued tasks, which must call its `callback(err)` argument when finished, with | ||
an optional `err` argument. | ||
@@ -1315,3 +1356,3 @@ * `payload` - An optional `integer` for determining how many tasks should be | ||
* `push(task, [callback])` - Adds `task` to the `queue`. The callback is called | ||
once the `worker` has finished processing the task. Instead of a single task, an array of `tasks` | ||
once the `worker` has finished processing the task. Instead of a single task, an array of `tasks` | ||
can be submitted. The respective callback is used for every task in the list. | ||
@@ -1321,2 +1362,3 @@ * `saturated` - A callback that is called when the `queue.length()` hits the concurrency and further tasks will be queued. | ||
* `drain` - A callback that is called when the last item from the `queue` has returned from the `worker`. | ||
* `idle()`, `pause()`, `resume()`, `kill()` - cargo inherits all of the same methods and event calbacks as [`queue`](#queue) | ||
@@ -1354,14 +1396,14 @@ __Example__ | ||
Determines the best order for running the functions in `tasks`, based on their | ||
requirements. Each function can optionally depend on other functions being completed | ||
first, and each function is run as soon as its requirements are satisfied. | ||
Determines the best order for running the functions in `tasks`, based on their | ||
requirements. Each function can optionally depend on other functions being completed | ||
first, and each function is run as soon as its requirements are satisfied. | ||
If any of the functions pass an error to their callback, it will not | ||
complete (so any other functions depending on it will not run), and the main | ||
`callback` is immediately called with the error. Functions also receive an | ||
If any of the functions pass an error to their callback, it will not | ||
complete (so any other functions depending on it will not run), and the main | ||
`callback` is immediately called with the error. Functions also receive an | ||
object containing the results of functions which have completed so far. | ||
Note, all functions are called with a `results` object as a second argument, | ||
Note, all functions are called with a `results` object as a second argument, | ||
so it is unsafe to pass functions in the `tasks` object which cannot handle the | ||
extra argument. | ||
extra argument. | ||
@@ -1383,3 +1425,3 @@ For example, this snippet of code: | ||
Instead, wrap the call to `readFile` in a function which does not forward the | ||
Instead, wrap the call to `readFile` in a function which does not forward the | ||
`results` object: | ||
@@ -1401,9 +1443,9 @@ | ||
i.e. can be used when specifying requirements for other tasks. | ||
The function receives two arguments: (1) a `callback(err, result)` which must be | ||
called when finished, passing an `error` (which can be `null`) and the result of | ||
The function receives two arguments: (1) a `callback(err, result)` which must be | ||
called when finished, passing an `error` (which can be `null`) and the result of | ||
the function's execution, and (2) a `results` object, containing the results of | ||
the previously executed functions. | ||
* `callback(err, results)` - An optional callback which is called when all the | ||
tasks have been completed. It receives the `err` argument if any `tasks` | ||
pass an error to their callback. Results are always returned; however, if | ||
tasks have been completed. It receives the `err` argument if any `tasks` | ||
pass an error to their callback. Results are always returned; however, if | ||
an error occurs, no further `tasks` will be performed, and the results | ||
@@ -1499,3 +1541,3 @@ object will only contain partial results. | ||
* `task(callback, results)` - A function which receives two arguments: (1) a `callback(err, result)` | ||
which must be called when finished, passing `err` (which can be `null`) and the `result` of | ||
which must be called when finished, passing `err` (which can be `null`) and the `result` of | ||
the function's execution, and (2) a `results` object, containing the results of | ||
@@ -1569,3 +1611,3 @@ the previously executed functions (if nested inside another control flow). | ||
Creates a continuation function with some arguments already applied. | ||
Creates a continuation function with some arguments already applied. | ||
@@ -1644,5 +1686,5 @@ Useful as a shorthand when combined with other control flow functions. Any arguments | ||
<a name="times" /> | ||
### times(n, callback) | ||
### times(n, iterator, [callback]) | ||
Calls the `callback` function `n` times, and accumulates results in the same manner | ||
Calls the `iterator` function `n` times, and accumulates results in the same manner | ||
you would use with [`map`](#map). | ||
@@ -1676,6 +1718,6 @@ | ||
<a name="timesSeries" /> | ||
### timesSeries(n, callback) | ||
### timesSeries(n, iterator, [callback]) | ||
The same as [`times`](#times), only the iterator is applied to each item in `arr` in | ||
series. The next `iterator` is only called once the current one has completed. | ||
series. The next `iterator` is only called once the current one has completed. | ||
The results array will be in the same order as the original. | ||
@@ -1730,2 +1772,37 @@ | ||
--------------------------------------- | ||
<a name="ensureAsync" /> | ||
### ensureAsync(fn) | ||
Wrap an async function and ensure it calls its callback on a later tick of the event loop. If the function already calls its callback on a next tick, no extra deferral is added. This is useful for preventing stack overflows (`RangeError: Maximum call stack size exceeded`) and generally keeping [Zalgo](http://blog.izs.me/post/59142742143/designing-apis-for-asynchrony) contained. | ||
__Arguments__ | ||
* `fn` - an async function, one that expects a node-style callback as its last argument | ||
Returns a wrapped function with the exact same call signature as the function passed in. | ||
__Example__ | ||
```js | ||
function sometimesAsync(arg, callback) { | ||
if (cache[arg]) { | ||
return callback(null, cache[arg]); // this would be synchronous!! | ||
} else { | ||
doSomeIO(arg, callback); // this IO would be asynchronous | ||
} | ||
} | ||
// this has a risk of stack overflows if many results are cached in a row | ||
async.mapSeries(args, sometimesAsync, done); | ||
// this will defer sometimesAsync's callback if necessary, | ||
// preventing stack overflows | ||
async.mapSeries(args, async.ensureAsync(sometimesAsync), done); | ||
``` | ||
--------------------------------------- | ||
<a name="log" /> | ||
@@ -1732,0 +1809,0 @@ ### log(function, arguments) |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
506740
28
1924
1798
0
9