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
174 lines
2.9 KiB
4 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;
|
||
|
};
|