function Parallel(asyncFncWrapper) {
    asyncFncWrapper = getAsyncFncWrapper(asyncFncWrapper);
    
    this.add = function() {
        var fnc, args;
        if(arguments.length===1) {
            fnc=arguments[0];
        }
        else if(arguments.length > 1) {
            fnc=arguments[arguments.length - 1];
            args = [];
            for(var i=0;i<arguments.length-1;i++){
                args.push(arguments[i]);
            }
        }
        
        if(typeof fnc!=='function')
            throw new Error('Async Parallel: Last argument have to be function(arg1, arg2, ..., next).');
        
        this._fncQueue = this._fncQueue || [];
        this._fncQueue.push({ args:args, fnc:fnc });
        
        this.execute = function(finalFnc){
            var fncQueue = this._fncQueue;
            fncQueue.finished = 0;
            
            function run(itm) {
                asyncFncWrapper(function(){
                    if(itm.args) {
                        itm.args.push(next);
                        itm.fnc.apply(itm, itm.args);
                    }
                    else itm.fnc(next);
                });
            }
            
            function next(){
                if(arguments.length > 0)
                    throw new Error('Async Parallel: next() callback do not accept any arguments, ' +
                                    'because of parallel behaviour, it cannot stop executing on error argument in callback');
                
                else if(!fncQueue) // TODO: implement trackign finished queue items // (fncQueue.scheduled !== fncQueue.current)
                    throw new Error('Async Parallel: cannot execute next() more than once per function');
                
                else if(fncQueue.finished + 1 < fncQueue.length) {
                    fncQueue.finished+=1;
                }
                else {
                    fncQueue = null;
                    if(typeof finalFnc==='function')
                        asyncFncWrapper(finalFnc);
                }
            }
            
            for(var i=0; i< (fncQueue || []).length; i++) {
                run(fncQueue[i]);
            }
        };
        return this;
    };
    
    this.execute = function(fnc){ if(typeof fnc==='function') asyncFncWrapper(fnc); };
}
function Parallel(asyncFncWrapper) {
    asyncFncWrapper = getAsyncFncWrapper(asyncFncWrapper);
    
    this.add = function() {
        var fnc, args;
        if(arguments.length===1) {
            fnc=arguments[0];
        }
        else if(arguments.length > 1) {
            fnc=arguments[arguments.length - 1];
            args = [];
            for(var i=0;i<arguments.length-1;i++){
                args.push(arguments[i]);
            }
        }
        
        if(typeof fnc!=='function')
            throw new Error('Async Parallel: Last argument have to be function(arg1, arg2, ..., next).');
        
        this._fncQueue = this._fncQueue || [];
        this._fncQueue.push({ args:args, fnc:fnc });
        
        this.execute = function(finalFnc){
            var fncQueue = this._fncQueue;
            fncQueue.finished = 0;
            
            function run(itm) {
                asyncFncWrapper(function(){
                    if(itm.args) {
                        itm.args.push(next);
                        itm.fnc.apply(itm, itm.args);
                    }
                    else itm.fnc(next);
                });
            }
            
            function next(){
                if(arguments.length > 0)
                    throw new Error('Async Parallel: next() callback do not accept any arguments, ' +
                                    'because of parallel behaviour, it cannot stop executing on error argument in callback');
                
                else if(!fncQueue) 
                    throw new Error('Async Parallel: cannot execute next() more than once per function');
                
                else if(fncQueue.finished + 1 < fncQueue.length) {
                    fncQueue.finished+=1;
                }
                else {
                    fncQueue = null;
                    if(typeof finalFnc==='function')
                        asyncFncWrapper(finalFnc);
                }
            }
            
            for(var i=0; i< (fncQueue || []).length; i++) {
                run(fncQueue[i]);
            }
        };
        return this;
    };
    
    this.execute = function(fnc){ if(typeof fnc==='function') asyncFncWrapper(fnc); };
}
function Parallel ( asyncFncWrapper ) { asyncFncWrapper = getAsyncFncWrapper ( asyncFncWrapper ) ; this . add = function ( ) { var fnc , args ; if ( arguments . length === 1 ) { fnc = arguments [ 0 ] ; } else if ( arguments . length > 1 ) { fnc = arguments [ arguments . length - 1 ] ; args = [ ] ; for ( var i = 0 ; i < arguments . length - 1 ; i ++ ) { args . push ( arguments [ i ] ) ; } } if ( typeof fnc !== 'function' ) throw new Error ( 'Async Parallel: Last argument have to be function(arg1, arg2, ..., next).' ) ; this . _fncQueue = this . _fncQueue || [ ] ; this . _fncQueue . push ( { args : args , fnc : fnc } ) ; this . execute = function ( finalFnc ) { var fncQueue = this . _fncQueue ; fncQueue . finished = 0 ; function run ( itm ) { asyncFncWrapper ( function ( ) { if ( itm . args ) { itm . args . push ( next ) ; itm . fnc . apply ( itm , itm . args ) ; } else itm . fnc ( next ) ; } ) ; } function next ( ) { if ( arguments . length > 0 ) throw new Error ( 'Async Parallel: next() callback do not accept any arguments, ' + 'because of parallel behaviour, it cannot stop executing on error argument in callback' ) ; else if ( ! fncQueue ) // TODO: implement trackign finished queue items // (fncQueue.scheduled !== fncQueue.current) throw new Error ( 'Async Parallel: cannot execute next() more than once per function' ) ; else if ( fncQueue . finished + 1 < fncQueue . length ) { fncQueue . finished += 1 ; } else { fncQueue = null ; if ( typeof finalFnc === 'function' ) asyncFncWrapper ( finalFnc ) ; } } for ( var i = 0 ; i < ( fncQueue || [ ] ) . length ; i ++ ) { run ( fncQueue [ i ] ) ; } } ; return this ; } ; this . execute = function ( fnc ) { if ( typeof fnc === 'function' ) asyncFncWrapper ( fnc ) ; } ; }
async Parallel queue constructor
@param {Object} asyncFncWrapper (optional) by default is setImmediate, but can be setTimeout, or any custom function
@returns {Object}  parallel queue
async Parallel queue constructor