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.

174 lines
2.9 KiB

2 weeks ago
/**
* 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;
};