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;
 | |
| };
 |