You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
173 lines
2.9 KiB
173 lines
2.9 KiB
/** |
|
* Module dependencies. |
|
*/ |
|
|
|
try { |
|
var EventEmitter = require('events').EventEmitter; |
|
if (!EventEmitter) throw new Error(); |
|
} catch (err) { |
|
var Emitter = require('emitter'); |
|
} |
|
|
|
/** |
|
* Defer. |
|
*/ |
|
|
|
var defer = typeof process !== 'undefined' && process && typeof process.nextTick === 'function' |
|
? process.nextTick |
|
: function(fn){ setTimeout(fn); }; |
|
|
|
/** |
|
* Noop. |
|
*/ |
|
|
|
function noop(){} |
|
|
|
/** |
|
* Expose `Batch`. |
|
*/ |
|
|
|
module.exports = Batch; |
|
|
|
/** |
|
* Create a new Batch. |
|
*/ |
|
|
|
function Batch() { |
|
if (!(this instanceof Batch)) return new Batch; |
|
this.fns = []; |
|
this.concurrency(Infinity); |
|
this.throws(true); |
|
for (var i = 0, len = arguments.length; i < len; ++i) { |
|
this.push(arguments[i]); |
|
} |
|
} |
|
|
|
/** |
|
* Inherit from `EventEmitter.prototype`. |
|
*/ |
|
|
|
if (EventEmitter) { |
|
Batch.prototype.__proto__ = EventEmitter.prototype; |
|
} else { |
|
Emitter(Batch.prototype); |
|
} |
|
|
|
/** |
|
* Set concurrency to `n`. |
|
* |
|
* @param {Number} n |
|
* @return {Batch} |
|
* @api public |
|
*/ |
|
|
|
Batch.prototype.concurrency = function(n){ |
|
this.n = n; |
|
return this; |
|
}; |
|
|
|
/** |
|
* Queue a function. |
|
* |
|
* @param {Function} fn |
|
* @return {Batch} |
|
* @api public |
|
*/ |
|
|
|
Batch.prototype.push = function(fn){ |
|
this.fns.push(fn); |
|
return this; |
|
}; |
|
|
|
/** |
|
* Set wether Batch will or will not throw up. |
|
* |
|
* @param {Boolean} throws |
|
* @return {Batch} |
|
* @api public |
|
*/ |
|
Batch.prototype.throws = function(throws) { |
|
this.e = !!throws; |
|
return this; |
|
}; |
|
|
|
/** |
|
* Execute all queued functions in parallel, |
|
* executing `cb(err, results)`. |
|
* |
|
* @param {Function} cb |
|
* @return {Batch} |
|
* @api public |
|
*/ |
|
|
|
Batch.prototype.end = function(cb){ |
|
var self = this |
|
, total = this.fns.length |
|
, pending = total |
|
, results = [] |
|
, errors = [] |
|
, cb = cb || noop |
|
, fns = this.fns |
|
, max = this.n |
|
, throws = this.e |
|
, index = 0 |
|
, done; |
|
|
|
// empty |
|
if (!fns.length) return defer(function(){ |
|
cb(null, results); |
|
}); |
|
|
|
// process |
|
function next() { |
|
var i = index++; |
|
var fn = fns[i]; |
|
if (!fn) return; |
|
var start = new Date; |
|
|
|
try { |
|
fn(callback); |
|
} catch (err) { |
|
callback(err); |
|
} |
|
|
|
function callback(err, res){ |
|
if (done) return; |
|
if (err && throws) return done = true, defer(function(){ |
|
cb(err); |
|
}); |
|
var complete = total - pending + 1; |
|
var end = new Date; |
|
|
|
results[i] = res; |
|
errors[i] = err; |
|
|
|
self.emit('progress', { |
|
index: i, |
|
value: res, |
|
error: err, |
|
pending: pending, |
|
total: total, |
|
complete: complete, |
|
percent: complete / total * 100 | 0, |
|
start: start, |
|
end: end, |
|
duration: end - start |
|
}); |
|
|
|
if (--pending) next(); |
|
else defer(function(){ |
|
if(!throws) cb(errors, results); |
|
else cb(null, results); |
|
}); |
|
} |
|
} |
|
|
|
// concurrency |
|
for (var i = 0; i < fns.length; i++) { |
|
if (i == max) break; |
|
next(); |
|
} |
|
|
|
return this; |
|
};
|
|
|