| /*global setImmediate: false, setTimeout: false, console: false */ |
| (function () { |
| |
| var async = {}; |
| |
| // global on the server, window in the browser |
| var root, previous_async; |
| |
| root = this; |
| if (root != null) { |
| previous_async = root.async; |
| } |
| |
| async.noConflict = function () { |
| root.async = previous_async; |
| return async; |
| }; |
| |
| function only_once(fn) { |
| var called = false; |
| return function() { |
| if (called) throw new Error("Callback was already called."); |
| called = true; |
| fn.apply(root, arguments); |
| } |
| } |
| |
| //// cross-browser compatiblity functions //// |
| |
| var _each = function (arr, iterator) { |
| if (arr.forEach) { |
| return arr.forEach(iterator); |
| } |
| for (var i = 0; i < arr.length; i += 1) { |
| iterator(arr[i], i, arr); |
| } |
| }; |
| |
| var _map = function (arr, iterator) { |
| if (arr.map) { |
| return arr.map(iterator); |
| } |
| var results = []; |
| _each(arr, function (x, i, a) { |
| results.push(iterator(x, i, a)); |
| }); |
| return results; |
| }; |
| |
| var _reduce = function (arr, iterator, memo) { |
| if (arr.reduce) { |
| return arr.reduce(iterator, memo); |
| } |
| _each(arr, function (x, i, a) { |
| memo = iterator(memo, x, i, a); |
| }); |
| return memo; |
| }; |
| |
| var _keys = function (obj) { |
| if (Object.keys) { |
| return Object.keys(obj); |
| } |
| var keys = []; |
| for (var k in obj) { |
| if (obj.hasOwnProperty(k)) { |
| keys.push(k); |
| } |
| } |
| return keys; |
| }; |
| |
| //// exported async module functions //// |
| |
| //// nextTick implementation with browser-compatible fallback //// |
| if (typeof process === 'undefined' || !(process.nextTick)) { |
| if (typeof setImmediate === 'function') { |
| async.setImmediate = setImmediate; |
| async.nextTick = setImmediate; |
| } |
| else { |
| async.setImmediate = async.nextTick; |
| async.nextTick = function (fn) { |
| setTimeout(fn, 0); |
| }; |
| } |
| } |
| else { |
| async.nextTick = process.nextTick; |
| async.setImmediate = setImmediate; |
| } |
| |
| async.each = function (arr, iterator, callback) { |
| callback = callback || function () {}; |
| if (!arr.length) { |
| return callback(); |
| } |
| var completed = 0; |
| _each(arr, function (x) { |
| iterator(x, only_once(function (err) { |
| if (err) { |
| callback(err); |
| callback = function () {}; |
| } |
| else { |
| completed += 1; |
| if (completed >= arr.length) { |
| callback(null); |
| } |
| } |
| })); |
| }); |
| }; |
| async.forEach = async.each; |
| |
| async.eachSeries = function (arr, iterator, callback) { |
| callback = callback || function () {}; |
| if (!arr.length) { |
| return callback(); |
| } |
| var completed = 0; |
| var iterate = function () { |
| iterator(arr[completed], function (err) { |
| if (err) { |
| callback(err); |
| callback = function () {}; |
| } |
| else { |
| completed += 1; |
| if (completed >= arr.length) { |
| callback(null); |
| } |
| else { |
| iterate(); |
| } |
| } |
| }); |
| }; |
| iterate(); |
| }; |
| async.forEachSeries = async.eachSeries; |
| |
| async.eachLimit = function (arr, limit, iterator, callback) { |
| var fn = _eachLimit(limit); |
| fn.apply(null, [arr, iterator, callback]); |
| }; |
| async.forEachLimit = async.eachLimit; |
| |
| var _eachLimit = function (limit) { |
| |
| return function (arr, iterator, callback) { |
| callback = callback || function () {}; |
| 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 = function () {}; |
| } |
| else { |
| completed += 1; |
| running -= 1; |
| if (completed >= arr.length) { |
| callback(); |
| } |
| else { |
| replenish(); |
| } |
| } |
| }); |
| } |
| })(); |
| }; |
| }; |
| |
| |
| var doParallel = function (fn) { |
| return function () { |
| var args = Array.prototype.slice.call(arguments); |
| return fn.apply(null, [async.each].concat(args)); |
| }; |
| }; |
| var doParallelLimit = function(limit, fn) { |
| return function () { |
| var args = Array.prototype.slice.call(arguments); |
| return fn.apply(null, [_eachLimit(limit)].concat(args)); |
| }; |
| }; |
| var doSeries = function (fn) { |
| return function () { |
| var args = Array.prototype.slice.call(arguments); |
| return fn.apply(null, [async.eachSeries].concat(args)); |
| }; |
| }; |
| |
| |
| var _asyncMap = function (eachfn, arr, iterator, callback) { |
| var results = []; |
| arr = _map(arr, function (x, i) { |
| return {index: i, value: x}; |
| }); |
| 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); |
| async.mapSeries = doSeries(_asyncMap); |
| async.mapLimit = function (arr, limit, iterator, callback) { |
| return _mapLimit(limit)(arr, iterator, callback); |
| }; |
| |
| var _mapLimit = function(limit) { |
| return doParallelLimit(limit, _asyncMap); |
| }; |
| |
| // reduce only has a series version, as doing reduce in parallel won't |
| // work in many situations. |
| async.reduce = function (arr, memo, iterator, callback) { |
| async.eachSeries(arr, function (x, callback) { |
| iterator(memo, x, function (err, v) { |
| memo = v; |
| callback(err); |
| }); |
| }, function (err) { |
| callback(err, memo); |
| }); |
| }; |
| // inject alias |
| async.inject = async.reduce; |
| // foldl alias |
| async.foldl = async.reduce; |
| |
| async.reduceRight = function (arr, memo, iterator, callback) { |
| var reversed = _map(arr, function (x) { |
| return x; |
| }).reverse(); |
| async.reduce(reversed, memo, iterator, callback); |
| }; |
| // foldr alias |
| async.foldr = async.reduceRight; |
| |
| var _filter = function (eachfn, arr, iterator, callback) { |
| var results = []; |
| arr = _map(arr, function (x, i) { |
| return {index: i, value: x}; |
| }); |
| eachfn(arr, function (x, callback) { |
| iterator(x.value, function (v) { |
| if (v) { |
| results.push(x); |
| } |
| callback(); |
| }); |
| }, function (err) { |
| callback(_map(results.sort(function (a, b) { |
| return a.index - b.index; |
| }), function (x) { |
| return x.value; |
| })); |
| }); |
| }; |
| async.filter = doParallel(_filter); |
| async.filterSeries = doSeries(_filter); |
| // select alias |
| async.select = async.filter; |
| async.selectSeries = async.filterSeries; |
| |
| var _reject = function (eachfn, arr, iterator, callback) { |
| var results = []; |
| arr = _map(arr, function (x, i) { |
| return {index: i, value: x}; |
| }); |
| eachfn(arr, function (x, callback) { |
| iterator(x.value, function (v) { |
| if (!v) { |
| results.push(x); |
| } |
| callback(); |
| }); |
| }, function (err) { |
| callback(_map(results.sort(function (a, b) { |
| return a.index - b.index; |
| }), function (x) { |
| return x.value; |
| })); |
| }); |
| }; |
| async.reject = doParallel(_reject); |
| async.rejectSeries = doSeries(_reject); |
| |
| var _detect = function (eachfn, arr, iterator, main_callback) { |
| eachfn(arr, function (x, callback) { |
| iterator(x, function (result) { |
| if (result) { |
| main_callback(x); |
| main_callback = function () {}; |
| } |
| else { |
| callback(); |
| } |
| }); |
| }, function (err) { |
| main_callback(); |
| }); |
| }; |
| async.detect = doParallel(_detect); |
| async.detectSeries = doSeries(_detect); |
| |
| async.some = function (arr, iterator, main_callback) { |
| async.each(arr, function (x, callback) { |
| iterator(x, function (v) { |
| if (v) { |
| main_callback(true); |
| main_callback = function () {}; |
| } |
| callback(); |
| }); |
| }, function (err) { |
| main_callback(false); |
| }); |
| }; |
| // any alias |
| async.any = async.some; |
| |
| async.every = function (arr, iterator, main_callback) { |
| async.each(arr, function (x, callback) { |
| iterator(x, function (v) { |
| if (!v) { |
| main_callback(false); |
| main_callback = function () {}; |
| } |
| callback(); |
| }); |
| }, function (err) { |
| main_callback(true); |
| }); |
| }; |
| // all alias |
| async.all = async.every; |
| |
| async.sortBy = function (arr, iterator, callback) { |
| async.map(arr, function (x, callback) { |
| iterator(x, function (err, criteria) { |
| if (err) { |
| callback(err); |
| } |
| else { |
| callback(null, {value: x, criteria: criteria}); |
| } |
| }); |
| }, function (err, results) { |
| if (err) { |
| return callback(err); |
| } |
| 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) { |
| return x.value; |
| })); |
| } |
| }); |
| }; |
| |
| async.auto = function (tasks, callback) { |
| callback = callback || function () {}; |
| var keys = _keys(tasks); |
| if (!keys.length) { |
| return callback(null); |
| } |
| |
| var results = {}; |
| |
| var listeners = []; |
| var addListener = function (fn) { |
| listeners.unshift(fn); |
| }; |
| var removeListener = function (fn) { |
| for (var i = 0; i < listeners.length; i += 1) { |
| if (listeners[i] === fn) { |
| listeners.splice(i, 1); |
| return; |
| } |
| } |
| }; |
| var taskComplete = function () { |
| _each(listeners.slice(0), function (fn) { |
| fn(); |
| }); |
| }; |
| |
| addListener(function () { |
| if (_keys(results).length === keys.length) { |
| callback(null, results); |
| callback = function () {}; |
| } |
| }); |
| |
| _each(keys, function (k) { |
| var task = (tasks[k] instanceof Function) ? [tasks[k]]: tasks[k]; |
| var taskCallback = function (err) { |
| var args = Array.prototype.slice.call(arguments, 1); |
| if (args.length <= 1) { |
| args = args[0]; |
| } |
| if (err) { |
| var safeResults = {}; |
| _each(_keys(results), function(rkey) { |
| safeResults[rkey] = results[rkey]; |
| }); |
| safeResults[k] = args; |
| callback(err, safeResults); |
| // stop subsequent errors hitting callback multiple times |
| callback = function () {}; |
| } |
| else { |
| results[k] = args; |
| async.setImmediate(taskComplete); |
| } |
| }; |
| var requires = task.slice(0, Math.abs(task.length - 1)) || []; |
| var ready = function () { |
| return _reduce(requires, function (a, x) { |
| return (a && results.hasOwnProperty(x)); |
| }, true) && !results.hasOwnProperty(k); |
| }; |
| if (ready()) { |
| task[task.length - 1](taskCallback, results); |
| } |
| else { |
| var listener = function () { |
| if (ready()) { |
| removeListener(listener); |
| task[task.length - 1](taskCallback, results); |
| } |
| }; |
| addListener(listener); |
| } |
| }); |
| }; |
| |
| async.waterfall = function (tasks, callback) { |
| callback = callback || function () {}; |
| if (tasks.constructor !== Array) { |
| var err = new Error('First argument to waterfall must be an array of functions'); |
| return callback(err); |
| } |
| if (!tasks.length) { |
| return callback(); |
| } |
| var wrapIterator = function (iterator) { |
| return function (err) { |
| if (err) { |
| callback.apply(null, arguments); |
| callback = function () {}; |
| } |
| else { |
| var args = Array.prototype.slice.call(arguments, 1); |
| var next = iterator.next(); |
| if (next) { |
| args.push(wrapIterator(next)); |
| } |
| else { |
| args.push(callback); |
| } |
| async.setImmediate(function () { |
| iterator.apply(null, args); |
| }); |
| } |
| }; |
| }; |
| wrapIterator(async.iterator(tasks))(); |
| }; |
| |
| var _parallel = function(eachfn, tasks, callback) { |
| callback = callback || function () {}; |
| if (tasks.constructor === Array) { |
| eachfn.map(tasks, function (fn, callback) { |
| if (fn) { |
| fn(function (err) { |
| var args = Array.prototype.slice.call(arguments, 1); |
| if (args.length <= 1) { |
| args = args[0]; |
| } |
| callback.call(null, err, args); |
| }); |
| } |
| }, callback); |
| } |
| else { |
| var results = {}; |
| eachfn.each(_keys(tasks), function (k, callback) { |
| tasks[k](function (err) { |
| var args = Array.prototype.slice.call(arguments, 1); |
| if (args.length <= 1) { |
| args = args[0]; |
| } |
| results[k] = args; |
| callback(err); |
| }); |
| }, function (err) { |
| callback(err, results); |
| }); |
| } |
| }; |
| |
| async.parallel = function (tasks, callback) { |
| _parallel({ map: async.map, each: async.each }, tasks, callback); |
| }; |
| |
| async.parallelLimit = function(tasks, limit, callback) { |
| _parallel({ map: _mapLimit(limit), each: _eachLimit(limit) }, tasks, callback); |
| }; |
| |
| async.series = function (tasks, callback) { |
| callback = callback || function () {}; |
| if (tasks.constructor === Array) { |
| async.mapSeries(tasks, function (fn, callback) { |
| if (fn) { |
| fn(function (err) { |
| var args = Array.prototype.slice.call(arguments, 1); |
| if (args.length <= 1) { |
| args = args[0]; |
| } |
| callback.call(null, err, args); |
| }); |
| } |
| }, callback); |
| } |
| else { |
| var results = {}; |
| async.eachSeries(_keys(tasks), function (k, callback) { |
| tasks[k](function (err) { |
| var args = Array.prototype.slice.call(arguments, 1); |
| if (args.length <= 1) { |
| args = args[0]; |
| } |
| results[k] = args; |
| callback(err); |
| }); |
| }, function (err) { |
| callback(err, results); |
| }); |
| } |
| }; |
| |
| async.iterator = function (tasks) { |
| var makeCallback = function (index) { |
| var fn = function () { |
| if (tasks.length) { |
| tasks[index].apply(null, arguments); |
| } |
| return fn.next(); |
| }; |
| fn.next = function () { |
| return (index < tasks.length - 1) ? makeCallback(index + 1): null; |
| }; |
| return fn; |
| }; |
| return makeCallback(0); |
| }; |
| |
| async.apply = function (fn) { |
| var args = Array.prototype.slice.call(arguments, 1); |
| return function () { |
| return fn.apply( |
| null, args.concat(Array.prototype.slice.call(arguments)) |
| ); |
| }; |
| }; |
| |
| var _concat = function (eachfn, arr, fn, callback) { |
| var r = []; |
| eachfn(arr, function (x, cb) { |
| fn(x, function (err, y) { |
| r = r.concat(y || []); |
| cb(err); |
| }); |
| }, function (err) { |
| callback(err, r); |
| }); |
| }; |
| async.concat = doParallel(_concat); |
| async.concatSeries = doSeries(_concat); |
| |
| async.whilst = function (test, iterator, callback) { |
| if (test()) { |
| iterator(function (err) { |
| if (err) { |
| return callback(err); |
| } |
| async.whilst(test, iterator, callback); |
| }); |
| } |
| else { |
| callback(); |
| } |
| }; |
| |
| async.doWhilst = function (iterator, test, callback) { |
| iterator(function (err) { |
| if (err) { |
| return callback(err); |
| } |
| if (test()) { |
| async.doWhilst(iterator, test, callback); |
| } |
| else { |
| callback(); |
| } |
| }); |
| }; |
| |
| async.until = function (test, iterator, callback) { |
| if (!test()) { |
| iterator(function (err) { |
| if (err) { |
| return callback(err); |
| } |
| async.until(test, iterator, callback); |
| }); |
| } |
| else { |
| callback(); |
| } |
| }; |
| |
| async.doUntil = function (iterator, test, callback) { |
| iterator(function (err) { |
| if (err) { |
| return callback(err); |
| } |
| if (!test()) { |
| async.doUntil(iterator, test, callback); |
| } |
| else { |
| callback(); |
| } |
| }); |
| }; |
| |
| async.queue = function (worker, concurrency) { |
| if (concurrency === undefined) { |
| concurrency = 1; |
| } |
| function _insert(q, data, pos, callback) { |
| if(data.constructor !== Array) { |
| data = [data]; |
| } |
| _each(data, function(task) { |
| var item = { |
| data: task, |
| callback: typeof callback === 'function' ? callback : null |
| }; |
| |
| if (pos) { |
| q.tasks.unshift(item); |
| } else { |
| q.tasks.push(item); |
| } |
| |
| if (q.saturated && q.tasks.length === concurrency) { |
| q.saturated(); |
| } |
| async.setImmediate(q.process); |
| }); |
| } |
| |
| var workers = 0; |
| var q = { |
| tasks: [], |
| concurrency: concurrency, |
| saturated: null, |
| empty: null, |
| drain: null, |
| push: function (data, callback) { |
| _insert(q, data, false, callback); |
| }, |
| unshift: function (data, callback) { |
| _insert(q, data, true, callback); |
| }, |
| process: function () { |
| if (workers < q.concurrency && q.tasks.length) { |
| var task = q.tasks.shift(); |
| if (q.empty && 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); |
| } |
| }, |
| length: function () { |
| return q.tasks.length; |
| }, |
| running: function () { |
| return workers; |
| } |
| }; |
| return q; |
| }; |
| |
| async.cargo = function (worker, payload) { |
| var working = false, |
| tasks = []; |
| |
| var cargo = { |
| tasks: tasks, |
| payload: payload, |
| saturated: null, |
| empty: null, |
| drain: null, |
| push: function (data, callback) { |
| if(data.constructor !== Array) { |
| data = [data]; |
| } |
| _each(data, function(task) { |
| tasks.push({ |
| data: task, |
| callback: typeof callback === 'function' ? callback : null |
| }); |
| 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.drain(); |
| return; |
| } |
| |
| var ts = typeof payload === 'number' |
| ? tasks.splice(0, payload) |
| : tasks.splice(0); |
| |
| 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; |
| }; |
| |
| var _console_fn = function (name) { |
| return function (fn) { |
| var args = Array.prototype.slice.call(arguments, 1); |
| fn.apply(null, args.concat([function (err) { |
| var args = Array.prototype.slice.call(arguments, 1); |
| if (typeof console !== 'undefined') { |
| if (err) { |
| if (console.error) { |
| console.error(err); |
| } |
| } |
| else if (console[name]) { |
| _each(args, function (x) { |
| console[name](x); |
| }); |
| } |
| } |
| }])); |
| }; |
| }; |
| async.log = _console_fn('log'); |
| async.dir = _console_fn('dir'); |
| /*async.info = _console_fn('info'); |
| async.warn = _console_fn('warn'); |
| async.error = _console_fn('error');*/ |
| |
| async.memoize = function (fn, hasher) { |
| var memo = {}; |
| var queues = {}; |
| hasher = hasher || function (x) { |
| return x; |
| }; |
| var memoized = function () { |
| var args = Array.prototype.slice.call(arguments); |
| var callback = args.pop(); |
| var key = hasher.apply(null, args); |
| if (key in memo) { |
| callback.apply(null, memo[key]); |
| } |
| else if (key in queues) { |
| queues[key].push(callback); |
| } |
| else { |
| queues[key] = [callback]; |
| fn.apply(null, args.concat([function () { |
| memo[key] = arguments; |
| var q = queues[key]; |
| delete queues[key]; |
| for (var i = 0, l = q.length; i < l; i++) { |
| q[i].apply(null, arguments); |
| } |
| }])); |
| } |
| }; |
| memoized.memo = memo; |
| memoized.unmemoized = fn; |
| return memoized; |
| }; |
| |
| async.unmemoize = function (fn) { |
| return function () { |
| return (fn.unmemoized || fn).apply(null, arguments); |
| }; |
| }; |
| |
| async.times = function (count, iterator, callback) { |
| var counter = []; |
| for (var i = 0; i < count; i++) { |
| counter.push(i); |
| } |
| return async.map(counter, iterator, callback); |
| }; |
| |
| async.timesSeries = function (count, iterator, callback) { |
| var counter = []; |
| for (var i = 0; i < count; i++) { |
| counter.push(i); |
| } |
| return async.mapSeries(counter, iterator, callback); |
| }; |
| |
| async.compose = function (/* functions... */) { |
| var fns = Array.prototype.reverse.call(arguments); |
| return function () { |
| var that = this; |
| var args = Array.prototype.slice.call(arguments); |
| var callback = args.pop(); |
| async.reduce(fns, args, function (newargs, fn, cb) { |
| fn.apply(that, newargs.concat([function () { |
| var err = arguments[0]; |
| var nextargs = Array.prototype.slice.call(arguments, 1); |
| cb(err, nextargs); |
| }])) |
| }, |
| function (err, results) { |
| callback.apply(that, [err].concat(results)); |
| }); |
| }; |
| }; |
| |
| var _applyEach = function (eachfn, fns /*args...*/) { |
| var go = function () { |
| var that = this; |
| var args = Array.prototype.slice.call(arguments); |
| var callback = args.pop(); |
| return eachfn(fns, function (fn, cb) { |
| fn.apply(that, args.concat([cb])); |
| }, |
| callback); |
| }; |
| if (arguments.length > 2) { |
| var args = Array.prototype.slice.call(arguments, 2); |
| return go.apply(this, args); |
| } |
| else { |
| return go; |
| } |
| }; |
| async.applyEach = doParallel(_applyEach); |
| async.applyEachSeries = doSeries(_applyEach); |
| |
| async.forever = function (fn, callback) { |
| function next(err) { |
| if (err) { |
| if (callback) { |
| return callback(err); |
| } |
| throw err; |
| } |
| fn(next); |
| } |
| next(); |
| }; |
| |
| // AMD / RequireJS |
| if (typeof define !== 'undefined' && define.amd) { |
| define([], function () { |
| return async; |
| }); |
| } |
| // Node.js |
| else if (typeof module !== 'undefined' && module.exports) { |
| module.exports = async; |
| } |
| // included directly via <script> tag |
| else { |
| root.async = async; |
| } |
| |
| }()); |