Spaces:
Running
Running
/** | |
* 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; | |
}; | |