174 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			174 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
/**
 | 
						|
 * 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;
 | 
						|
};
 |