Skip to content

Instantly share code, notes, and snippets.

@farandal
Last active January 4, 2016 06:49
Show Gist options
  • Save farandal/8584401 to your computer and use it in GitHub Desktop.
Save farandal/8584401 to your computer and use it in GitHub Desktop.
AMD porting of Async library. https://github.com/caolan/async
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