Last active
January 4, 2016 06:49
-
-
Save farandal/8584401 to your computer and use it in GitHub Desktop.
AMD porting of Async library. https://github.com/caolan/async
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
define('lib/async', [ | |
'ax/class', | |
'ax/util', | |
'app/model/api', | |
'ax/console', | |
'ax/config', | |
], | |
function (klass, util, api, console, xdkConfig) { | |
// Private | |
var async = {}; | |
var root, previous_async; | |
root = this; | |
if (root !== null) { | |
previous_async = root.async; | |
} | |
var noConflict = function () { | |
root.async = previous_async; | |
return async; | |
}; | |
var only_once = function (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') { | |
this.nextTick = function (fn) { | |
// not a direct alias for IE10 compatibility | |
setImmediate(fn); | |
}; | |
this.setImmediate = this.nextTick; | |
} else { | |
this.nextTick = function (fn) { | |
setTimeout(fn, 0); | |
}; | |
this.setImmediate = this.nextTick; | |
} | |
} else { | |
this.nextTick = process.nextTick; | |
if (typeof setImmediate !== 'undefined') { | |
this.setImmediate = setImmediate; | |
} else { | |
this.setImmediate = this.nextTick; | |
} | |
}*/ | |
if (typeof process === 'undefined' || !(process.nextTick)) { | |
var nextTick; | |
if (typeof setImmediate === 'function') { | |
var nextTick = function (fn) { | |
// not a direct alias for IE10 compatibility | |
setImmediate(fn); | |
}; | |
var setImmediate = nextTick; | |
} else { | |
var nextTick = function (fn) { | |
setTimeout(fn, 0); | |
}; | |
var setImmediate = nextTick; | |
} | |
} else { | |
var nextTick = process.nextTick; | |
if (typeof setImmediate !== 'undefined') { | |
var setImmediate = setImmediate; | |
} else { | |
var setImmediate = nextTick; | |
} | |
} | |
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, [this.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, [this.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); | |
}); | |
}; | |
var _mapLimit = function (limit) { | |
return doParallelLimit(limit, _asyncMap); | |
}; | |
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; | |
})); | |
}); | |
}; | |
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; | |
})); | |
}); | |
}; | |
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(); | |
}); | |
}; | |
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); | |
}); | |
} | |
}; | |
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); | |
}); | |
}; | |
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); | |
}); | |
} | |
} | |
} | |
])); | |
}; | |
}; | |
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; | |
} | |
}; | |
// Public | |
return { | |
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); | |
} | |
} | |
})); | |
}); | |
}, | |
forEach: this.each, | |
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(); | |
}, | |
forEachSeries: this.eachSeries, | |
eachLimit: function (arr, limit, iterator, callback) { | |
var fn = _eachLimit(limit); | |
fn.apply(null, [arr, iterator, callback]); | |
}, | |
forEachLimit: this.eachLimit, | |
map: doParallel(_asyncMap), | |
mapSeries: doSeries(_asyncMap), | |
mapLimit: function (arr, limit, iterator, callback) { | |
return _mapLimit(limit)(arr, iterator, callback); | |
}, | |
// reduce only has a series version, as doing reduce in parallel won't | |
// work in many situations. | |
reduce: function (arr, memo, iterator, callback) { | |
var self = this; | |
self.eachSeries(arr, function (x, callback) { | |
iterator(memo, x, function (err, v) { | |
memo = v; | |
callback(err); | |
}); | |
}, function (err) { | |
callback(err, memo); | |
}); | |
}, | |
// inject alias | |
inject: this.reduce, | |
// foldl alias | |
foldl: this.reduce, | |
reduceRight: function (arr, memo, iterator, callback) { | |
var self = this; | |
var reversed = _map(arr, function (x) { | |
return x; | |
}).reverse(); | |
self.reduce(reversed, memo, iterator, callback); | |
}, | |
// foldr alias | |
foldr: this.reduceRight, | |
filter: doParallel(_filter), | |
filterSeries: doSeries(_filter), | |
// select alias | |
select: this.filter, | |
selectSeries: this.filterSeries, | |
reject: doParallel(_reject), | |
rejectSeries: doSeries(_reject), | |
detect: doParallel(_detect), | |
detectSeries: doSeries(_detect), | |
some: function (arr, iterator, main_callback) { | |
var self = this; | |
self.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 | |
any: this.some, | |
every: function (arr, iterator, main_callback) { | |
var self = this; | |
self.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 | |
all: this.every, | |
sortBy: function (arr, iterator, callback) { | |
var self = this; | |
self.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; | |
})); | |
} | |
}); | |
}, | |
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; | |
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); | |
} | |
}); | |
}, | |
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); | |
} | |
setImmediate(function () { | |
iterator.apply(null, args); | |
}); | |
} | |
}; | |
}; | |
wrapIterator(async.iterator(tasks))(); | |
}, | |
concat: doParallel(_concat), | |
concatSeries: doSeries(_concat), | |
parallel: function (tasks, callback) { | |
var self = this; | |
_parallel({ | |
map: self.map, | |
each: self.each | |
}, tasks, callback); | |
}, | |
parallelLimit: function (tasks, limit, callback) { | |
_parallel({ | |
map: _mapLimit(limit), | |
each: _eachLimit(limit) | |
}, tasks, callback); | |
}, | |
series: function (tasks, callback) { | |
var self = this; | |
callback = callback || function () {}; | |
if (tasks.constructor === Array) { | |
self.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 = {}; | |
self.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); | |
}); | |
} | |
}, | |
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); | |
}, | |
apply: function (fn) { | |
var args = Array.prototype.slice.call(arguments, 1); | |
return function () { | |
return fn.apply( | |
null, args.concat(Array.prototype.slice.call(arguments)) | |
); | |
}; | |
}, | |
whilst: function (test, iterator, callback) { | |
var self = this; | |
if (test()) { | |
iterator(function (err) { | |
if (err) { | |
return callback(err); | |
} | |
self.whilst(test, iterator, callback); | |
}); | |
} else { | |
callback(); | |
} | |
}, | |
doWhilst: function (iterator, test, callback) { | |
var self = this; | |
iterator(function (err) { | |
if (err) { | |
return callback(err); | |
} | |
if (test()) { | |
self.doWhilst(iterator, test, callback); | |
} else { | |
callback(); | |
} | |
}); | |
}, | |
until: function (test, iterator, callback) { | |
var self = this; | |
if (!test()) { | |
iterator(function (err) { | |
if (err) { | |
return callback(err); | |
} | |
self.until(test, iterator, callback); | |
}); | |
} else { | |
callback(); | |
} | |
}, | |
doUntil: function (iterator, test, callback) { | |
var self = this; | |
iterator(function (err) { | |
if (err) { | |
return callback(err); | |
} | |
if (!test()) { | |
self.doUntil(iterator, test, callback); | |
} else { | |
callback(); | |
} | |
}); | |
}, | |
queue: function (worker, concurrency) { | |
var self = this; | |
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(); | |
} | |
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; | |
}, | |
cargo: function (worker, payload) { | |
var self = this; | |
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(); | |
} | |
}); | |
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; | |
}, | |
log: _console_fn('log'), | |
dir: _console_fn('dir'), | |
/*async.info = _console_fn('info'); | |
async.warn = _console_fn('warn'); | |
async.error = _console_fn('error');*/ | |
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; | |
}, | |
unmemoize: function (fn) { | |
return function () { | |
return (fn.unmemoized || fn).apply(null, arguments); | |
}; | |
}, | |
times: function (count, iterator, callback) { | |
var self = this; | |
var counter = []; | |
for (var i = 0; i < count; i++) { | |
counter.push(i); | |
} | |
return self.map(counter, iterator, callback); | |
}, | |
timesSeries: function (count, iterator, callback) { | |
var self = this; | |
var counter = []; | |
for (var i = 0; i < count; i++) { | |
counter.push(i); | |
} | |
return self.mapSeries(counter, iterator, callback); | |
}, | |
compose: function ( /* functions... */ ) { | |
var self = this; | |
var fns = Array.prototype.reverse.call(arguments); | |
return function () { | |
var that = this; | |
var args = Array.prototype.slice.call(arguments); | |
var callback = args.pop(); | |
self.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)); | |
}); | |
}; | |
}, | |
applyEach: doParallel(_applyEach), | |
applyEachSeries: doSeries(_applyEach), | |
forever: function (fn, callback) { | |
function next(err) { | |
if (err) { | |
if (callback) { | |
return callback(err); | |
} | |
throw err; | |
} | |
fn(next); | |
} | |
next(); | |
}, | |
}; | |
} | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment