function _execute(err) {
	var self = this;
	if (err) return this._finalize(err); // An error has been raised - stop exec and call finalize now

	if (!self._executing) { // Never before run this object - run fire('start') and defer until it finishes
		self._executing = true;
		return self.fire.call(self, 'start', self._execute.bind(self));
	}

	do {
		var redo = false;
		if (self._structPointer >= self._struct.length) return this._finalize(err); // Nothing more to execute in struct
		self._deferCheck(); // Kick off any pending deferred items
		var currentExec = self._struct[self._structPointer];
		// Sanity checks {{{
		if (!currentExec.type) {
			throw new Error('No type is specified for async-chainable structure at offset ' + self._structPointer);
			return self;
		}
		// }}}
		self._structPointer++;

		// Skip step when function supports skipping if the argument is empty {{{
		if (
			[
				'parallelArray', 'parallelObject',
				'forEachArray', 'forEachObject',
				'seriesArray', 'seriesObject',
				'deferArray', 'deferObject',
				'set'
			].indexOf(currentExec.type) > -1 &&
			(
				!currentExec.payload || // Not set OR
				(argy.isType(currentExec.payload, 'array') && !currentExec.payload.length) || // An empty array
				(argy.isType(currentExec.payload, 'object') && !Object.keys(currentExec.payload).length) // An empty object
			)
		) {
			currentExec.completed = true;
			redo = true;
			continue;
		}
		// }}}

		switch (currentExec.type) {
			case 'forEachRange':
				var iterArray = Array(currentExec.max - currentExec.min + 1).fill(false);
				self.runArray(iterArray.map(function(v, i) {
					var val = self._context._item = currentExec.min + i;
					var index = self._context._key = i;
					return function(next) {
						if (currentExec.translate) val = currentExec.translate(val, index, iterArray.length);
						run(self._options.context, currentExec.callback, next, [val, index, iterArray.length]);
					};
				}), self._options.limit, function(err) {
					currentExec.completed = true;
					self._execute(err);
				});
				break;
			case 'forEachArray':
				self.runArray(currentExec.payload.map(function(item, iter) {
					self._context._item = item;
					self._context._key = iter;
					return function(next) {
						run(self._options.context, currentExec.callback, next, [item, iter]);
					};
				}), self._options.limit, function(err) {
					currentExec.completed = true;
					self._execute(err);
				});
				break;
			case 'forEachObject':
				self.runArray(Object.keys(currentExec.payload).map(function(key) {
					return function(next) {
						self._context._item = currentExec.payload[key];
						self._context._key = key;
						run(self._options.context, currentExec.callback, function(err, value) {
							self._set(key, value); // Allocate returned value to context
							next(err);
						}, [currentExec.payload[key], key]);
					};
				}), self._options.limit, function(err) {
					currentExec.completed = true;
					self._execute(err);
				});
				break;
			case 'forEachLateBound':
				if (!currentExec.payload || !currentExec.payload.length) { // Payload is blank
					// Goto next chain
					currentExec.completed = true;
					redo = true;
					break;
				}

				var resolvedPayload = self.getPath(self._context, currentExec.payload);
				if (!resolvedPayload) { // Resolved payload is blank
					// Goto next chain
					currentExec.completed = true;
					redo = true;
					break;
				}

				// Replace own exec array with actual type of payload now we know what it is {{{
				if (argy.isType(resolvedPayload, 'array')) {
					currentExec.type = 'forEachArray';
				} else if (argy.isType(resolvedPayload, 'object')) {
					currentExec.type = 'forEachObject';
				} else {
					throw new Error('Cannot perform forEach over unknown object type: ' + argy.getType(resolvedPayload));
				}
				currentExec.payload = resolvedPayload;
				self._structPointer--; // Force re-eval of this chain item now its been replace with its real (late-bound) type
				redo = true;
				// }}}
				break;

			case 'mapArray':
				var output = new Array(currentExec.payload.length);
				self.runArray(currentExec.payload.map(function(item, iter) {
					self._context._item = item;
					self._context._key = iter;
					return function(next) {
						run(self._options.context, currentExec.callback, function(err, value) {
							if (err) return next(err);
							output[iter] = value;
							next();
						}, [item, iter]);
					};
				}), self._options.limit, function(err) {
					currentExec.completed = true;
					self._set(currentExec.output, output);
					self._execute(err);
				});
				break;
			case 'mapObject':
				var output = {};
				self.runArray(Object.keys(currentExec.payload).map(function(key) {
					return function(next) {
						self._context._item = currentExec.payload[key];
						self._context._key = key;
						run(self._options.context, currentExec.callback, function(err, value, outputKey) {
							output[outputKey || key] = value;
							next(err);
						}, [currentExec.payload[key], key]);
					};
				}), self._options.limit, function(err) {
					currentExec.completed = true;
					self._set(currentExec.output, output);
					self._execute(err);
				});
				break;
			case 'mapLateBound':
				if (!currentExec.payload || !currentExec.payload.length) { // Payload is blank
					// Goto next chain
					currentExec.completed = true;
					redo = true;
					break;
				}

				var resolvedPayload = self.getPath(self._context, currentExec.payload);
				if (!resolvedPayload) { // Resolved payload is blank
					// Goto next chain
					currentExec.completed = true;
					redo = true;
					break;
				}

				// Replace own exec array with actual type of payload now we know what it is {{{
				if (argy.isType(resolvedPayload, 'array')) {
					currentExec.type = 'mapArray';
				} else if (argy.isType(resolvedPayload, 'object')) {
					currentExec.type = 'mapObject';
				} else {
					throw new Error('Cannot perform map over unknown object type: ' + argy.getType(resolvedPayload));
				}
				currentExec.payload = resolvedPayload;
				self._structPointer--; // Force re-eval of this chain item now its been replace with its real (late-bound) type
				redo = true;
				// }}}
				break;
			case 'parallelArray':
			case 'seriesArray':
				self.runArray(currentExec.payload.map(function(task) {
					return function(next) {
						run(self._options.context, task, next);
					};
				}), currentExec.type == 'parallelArray' ? self._options.limit : 1, function(err) {
					currentExec.completed = true;
					self._execute(err);
				});
				break;
			case 'seriesObject':
			case 'parallelObject':
				self.runArray(Object.keys(currentExec.payload).map(function(key) {
					return function(next) {
						run(self._options.context, currentExec.payload[key], function(err, value) {
							self._set(key, value); // Allocate returned value to context
							next(err);
						});
					};
				}), currentExec.type == 'parallelObject' ? self._options.limit : 1, function(err) {
					currentExec.completed = true;
					self._execute(err);
				});
				break;
			case 'race':
				var hasResult = false;
				var hasError = false;
				self.runArray(currentExec.payload.map(function(task) {
					return function(next) {
						run(self._options.context, task, function(err, taskResult) {
							if (err) {
								hasError = true
								next(err, taskResult);
							} else if (!hasResult && !hasError && taskResult !== null && typeof taskResult !== 'undefined') {
								self._set(currentExec.id, taskResult); // Allocate returned value to context
								hasResult = true;
								next('!RACEDONE!', taskResult); // Force an error to stop the run() function
							} else {
								next(err, taskResult);
							}
						});
					};
				}), self._options.limit, function(err, val) {
					currentExec.completed = true;

					// Override race finish error as it was just to stop the race and not a real one
					if (err == '!RACEDONE!') return self._execute();

					self._execute(err);
				});
				break;
			case 'deferArray':
				currentExec.payload.forEach(function(task) {
					self._deferAdd(null, task, currentExec);
				});

				redo = true;
				break;
			case 'deferObject':
				Object.keys(currentExec.payload).forEach(function(key) {
					self._deferAdd(key, currentExec.payload[key], currentExec);
				});

				redo = true;
				break;
			case 'await': // Await can operate in two modes, either payload=[] (examine all) else (examine specific keys)
				if (!currentExec.payload.length) { // Check all tasks are complete
					if (self._struct.slice(0, self._structPointer - 1).every(function(stage) { // Examine all items UP TO self one and check they are complete
						return stage.completed;
					})) { // All tasks up to self point are marked as completed
						if (_deferTimeoutHandle) clearTimeout(_deferTimeoutHandle);
						currentExec.completed = true;
						redo = true;
					} else {
						self._structPointer--; // At least one task is outstanding - rewind to self stage so we repeat on next resolution
					}
				} else { // Check certain tasks are complete by key
					if (currentExec.payload.every(function(dep) { // Examine all named dependencies
						return !! self._context[dep];
					})) { // All are present
						if (_deferTimeoutHandle) clearTimeout(_deferTimeoutHandle);
						currentExec.completed = true;
						redo = true;
					} else {
						self._structPointer--; // At least one dependency is outstanding - rewind to self stage so we repeat on next resolution
					}
				}
				break;
			case 'limit': // Set the options.limit variable
				self._options.limit = currentExec.payload;
				currentExec.completed = true;
				redo = true; // Move on to next action
				break;
			case 'timeout': // Set the timeout function or its timeout value

				if (currentExec.delay === false) { // Disable
					self._options.timeout = false;
				} else {
					// Set the callback if one was passed
					if (currentExec.callback) self._options.timeoutHandler = currentExec.callback;

					// Set the delay if one was passed
					if (currentExec.delay) self._options.timeout = currentExec.delay;
				}

				currentExec.completed = true;
				redo = true; // Move to next action
				break;
			case 'context': // Change the self._options.context object
				self._options.context = currentExec.payload ? currentExec.payload : self._context; // Set context (if null use internal context)
				currentExec.completed = true;
				redo = true; // Move on to next action
				break;
			case 'set': // Set a hash of variables within context
				Object.keys(currentExec.payload).forEach(function(key) {
					self._set(key, currentExec.payload[key]);
				});
				currentExec.completed = true;
				redo = true; // Move on to next action
				break;
			case 'end': // self should ALWAYS be the last item in the structure and indicates the final function call
				this._finalize();
				break;
			default:
				if (this._plugins[currentExec.type]) { // Is there a plugin that should manage this?
					this._plugins[currentExec.type].call(this, currentExec);
				} else {
					throw new Error('Unknown async-chainable exec type: ' + currentExec.type);
				}
				return;
		}
	} while (redo);
}
function _execute(err) {
	var self = this;
	if (err) return this._finalize(err); 

	if (!self._executing) { 
		self._executing = true;
		return self.fire.call(self, 'start', self._execute.bind(self));
	}

	do {
		var redo = false;
		if (self._structPointer >= self._struct.length) return this._finalize(err); 
		self._deferCheck(); 
		var currentExec = self._struct[self._structPointer];
		
		if (!currentExec.type) {
			throw new Error('No type is specified for async-chainable structure at offset ' + self._structPointer);
			return self;
		}
		
		self._structPointer++;

		
		if (
			[
				'parallelArray', 'parallelObject',
				'forEachArray', 'forEachObject',
				'seriesArray', 'seriesObject',
				'deferArray', 'deferObject',
				'set'
			].indexOf(currentExec.type) > -1 &&
			(
				!currentExec.payload || 
				(argy.isType(currentExec.payload, 'array') && !currentExec.payload.length) || 
				(argy.isType(currentExec.payload, 'object') && !Object.keys(currentExec.payload).length) 
			)
		) {
			currentExec.completed = true;
			redo = true;
			continue;
		}
		

		switch (currentExec.type) {
			case 'forEachRange':
				var iterArray = Array(currentExec.max - currentExec.min + 1).fill(false);
				self.runArray(iterArray.map(function(v, i) {
					var val = self._context._item = currentExec.min + i;
					var index = self._context._key = i;
					return function(next) {
						if (currentExec.translate) val = currentExec.translate(val, index, iterArray.length);
						run(self._options.context, currentExec.callback, next, [val, index, iterArray.length]);
					};
				}), self._options.limit, function(err) {
					currentExec.completed = true;
					self._execute(err);
				});
				break;
			case 'forEachArray':
				self.runArray(currentExec.payload.map(function(item, iter) {
					self._context._item = item;
					self._context._key = iter;
					return function(next) {
						run(self._options.context, currentExec.callback, next, [item, iter]);
					};
				}), self._options.limit, function(err) {
					currentExec.completed = true;
					self._execute(err);
				});
				break;
			case 'forEachObject':
				self.runArray(Object.keys(currentExec.payload).map(function(key) {
					return function(next) {
						self._context._item = currentExec.payload[key];
						self._context._key = key;
						run(self._options.context, currentExec.callback, function(err, value) {
							self._set(key, value); 
							next(err);
						}, [currentExec.payload[key], key]);
					};
				}), self._options.limit, function(err) {
					currentExec.completed = true;
					self._execute(err);
				});
				break;
			case 'forEachLateBound':
				if (!currentExec.payload || !currentExec.payload.length) { 
					
					currentExec.completed = true;
					redo = true;
					break;
				}

				var resolvedPayload = self.getPath(self._context, currentExec.payload);
				if (!resolvedPayload) { 
					
					currentExec.completed = true;
					redo = true;
					break;
				}

				
				if (argy.isType(resolvedPayload, 'array')) {
					currentExec.type = 'forEachArray';
				} else if (argy.isType(resolvedPayload, 'object')) {
					currentExec.type = 'forEachObject';
				} else {
					throw new Error('Cannot perform forEach over unknown object type: ' + argy.getType(resolvedPayload));
				}
				currentExec.payload = resolvedPayload;
				self._structPointer--; 
				redo = true;
				
				break;

			case 'mapArray':
				var output = new Array(currentExec.payload.length);
				self.runArray(currentExec.payload.map(function(item, iter) {
					self._context._item = item;
					self._context._key = iter;
					return function(next) {
						run(self._options.context, currentExec.callback, function(err, value) {
							if (err) return next(err);
							output[iter] = value;
							next();
						}, [item, iter]);
					};
				}), self._options.limit, function(err) {
					currentExec.completed = true;
					self._set(currentExec.output, output);
					self._execute(err);
				});
				break;
			case 'mapObject':
				var output = {};
				self.runArray(Object.keys(currentExec.payload).map(function(key) {
					return function(next) {
						self._context._item = currentExec.payload[key];
						self._context._key = key;
						run(self._options.context, currentExec.callback, function(err, value, outputKey) {
							output[outputKey || key] = value;
							next(err);
						}, [currentExec.payload[key], key]);
					};
				}), self._options.limit, function(err) {
					currentExec.completed = true;
					self._set(currentExec.output, output);
					self._execute(err);
				});
				break;
			case 'mapLateBound':
				if (!currentExec.payload || !currentExec.payload.length) { 
					
					currentExec.completed = true;
					redo = true;
					break;
				}

				var resolvedPayload = self.getPath(self._context, currentExec.payload);
				if (!resolvedPayload) { 
					
					currentExec.completed = true;
					redo = true;
					break;
				}

				
				if (argy.isType(resolvedPayload, 'array')) {
					currentExec.type = 'mapArray';
				} else if (argy.isType(resolvedPayload, 'object')) {
					currentExec.type = 'mapObject';
				} else {
					throw new Error('Cannot perform map over unknown object type: ' + argy.getType(resolvedPayload));
				}
				currentExec.payload = resolvedPayload;
				self._structPointer--; 
				redo = true;
				
				break;
			case 'parallelArray':
			case 'seriesArray':
				self.runArray(currentExec.payload.map(function(task) {
					return function(next) {
						run(self._options.context, task, next);
					};
				}), currentExec.type == 'parallelArray' ? self._options.limit : 1, function(err) {
					currentExec.completed = true;
					self._execute(err);
				});
				break;
			case 'seriesObject':
			case 'parallelObject':
				self.runArray(Object.keys(currentExec.payload).map(function(key) {
					return function(next) {
						run(self._options.context, currentExec.payload[key], function(err, value) {
							self._set(key, value); 
							next(err);
						});
					};
				}), currentExec.type == 'parallelObject' ? self._options.limit : 1, function(err) {
					currentExec.completed = true;
					self._execute(err);
				});
				break;
			case 'race':
				var hasResult = false;
				var hasError = false;
				self.runArray(currentExec.payload.map(function(task) {
					return function(next) {
						run(self._options.context, task, function(err, taskResult) {
							if (err) {
								hasError = true
								next(err, taskResult);
							} else if (!hasResult && !hasError && taskResult !== null && typeof taskResult !== 'undefined') {
								self._set(currentExec.id, taskResult); 
								hasResult = true;
								next('!RACEDONE!', taskResult); 
							} else {
								next(err, taskResult);
							}
						});
					};
				}), self._options.limit, function(err, val) {
					currentExec.completed = true;

					
					if (err == '!RACEDONE!') return self._execute();

					self._execute(err);
				});
				break;
			case 'deferArray':
				currentExec.payload.forEach(function(task) {
					self._deferAdd(null, task, currentExec);
				});

				redo = true;
				break;
			case 'deferObject':
				Object.keys(currentExec.payload).forEach(function(key) {
					self._deferAdd(key, currentExec.payload[key], currentExec);
				});

				redo = true;
				break;
			case 'await': 
				if (!currentExec.payload.length) { 
					if (self._struct.slice(0, self._structPointer - 1).every(function(stage) { 
						return stage.completed;
					})) { 
						if (_deferTimeoutHandle) clearTimeout(_deferTimeoutHandle);
						currentExec.completed = true;
						redo = true;
					} else {
						self._structPointer--; 
					}
				} else { 
					if (currentExec.payload.every(function(dep) { 
						return !! self._context[dep];
					})) { 
						if (_deferTimeoutHandle) clearTimeout(_deferTimeoutHandle);
						currentExec.completed = true;
						redo = true;
					} else {
						self._structPointer--; 
					}
				}
				break;
			case 'limit': 
				self._options.limit = currentExec.payload;
				currentExec.completed = true;
				redo = true; 
				break;
			case 'timeout': 

				if (currentExec.delay === false) { 
					self._options.timeout = false;
				} else {
					
					if (currentExec.callback) self._options.timeoutHandler = currentExec.callback;

					
					if (currentExec.delay) self._options.timeout = currentExec.delay;
				}

				currentExec.completed = true;
				redo = true; 
				break;
			case 'context': 
				self._options.context = currentExec.payload ? currentExec.payload : self._context; 
				currentExec.completed = true;
				redo = true; 
				break;
			case 'set': 
				Object.keys(currentExec.payload).forEach(function(key) {
					self._set(key, currentExec.payload[key]);
				});
				currentExec.completed = true;
				redo = true; 
				break;
			case 'end': 
				this._finalize();
				break;
			default:
				if (this._plugins[currentExec.type]) { 
					this._plugins[currentExec.type].call(this, currentExec);
				} else {
					throw new Error('Unknown async-chainable exec type: ' + currentExec.type);
				}
				return;
		}
	} while (redo);
}
function _execute ( err ) { var self = this ; if ( err ) return this . _finalize ( err ) ; // An error has been raised - stop exec and call finalize now if ( ! self . _executing ) { // Never before run this object - run fire('start') and defer until it finishes self . _executing = true ; return self . fire . call ( self , 'start' , self . _execute . bind ( self ) ) ; } do { var redo = false ; if ( self . _structPointer >= self . _struct . length ) return this . _finalize ( err ) ; // Nothing more to execute in struct self . _deferCheck ( ) ; // Kick off any pending deferred items var currentExec = self . _struct [ self . _structPointer ] ; // Sanity checks {{{ if ( ! currentExec . type ) { throw new Error ( 'No type is specified for async-chainable structure at offset ' + self . _structPointer ) ; return self ; } // }}} self . _structPointer ++ ; // Skip step when function supports skipping if the argument is empty {{{ if ( [ 'parallelArray' , 'parallelObject' , 'forEachArray' , 'forEachObject' , 'seriesArray' , 'seriesObject' , 'deferArray' , 'deferObject' , 'set' ] . indexOf ( currentExec . type ) > - 1 && ( ! currentExec . payload || // Not set OR ( argy . isType ( currentExec . payload , 'array' ) && ! currentExec . payload . length ) || // An empty array ( argy . isType ( currentExec . payload , 'object' ) && ! Object . keys ( currentExec . payload ) . length ) // An empty object ) ) { currentExec . completed = true ; redo = true ; continue ; } // }}} switch ( currentExec . type ) { case 'forEachRange' : var iterArray = Array ( currentExec . max - currentExec . min + 1 ) . fill ( false ) ; self . runArray ( iterArray . map ( function ( v , i ) { var val = self . _context . _item = currentExec . min + i ; var index = self . _context . _key = i ; return function ( next ) { if ( currentExec . translate ) val = currentExec . translate ( val , index , iterArray . length ) ; run ( self . _options . context , currentExec . callback , next , [ val , index , iterArray . length ] ) ; } ; } ) , self . _options . limit , function ( err ) { currentExec . completed = true ; self . _execute ( err ) ; } ) ; break ; case 'forEachArray' : self . runArray ( currentExec . payload . map ( function ( item , iter ) { self . _context . _item = item ; self . _context . _key = iter ; return function ( next ) { run ( self . _options . context , currentExec . callback , next , [ item , iter ] ) ; } ; } ) , self . _options . limit , function ( err ) { currentExec . completed = true ; self . _execute ( err ) ; } ) ; break ; case 'forEachObject' : self . runArray ( Object . keys ( currentExec . payload ) . map ( function ( key ) { return function ( next ) { self . _context . _item = currentExec . payload [ key ] ; self . _context . _key = key ; run ( self . _options . context , currentExec . callback , function ( err , value ) { self . _set ( key , value ) ; // Allocate returned value to context next ( err ) ; } , [ currentExec . payload [ key ] , key ] ) ; } ; } ) , self . _options . limit , function ( err ) { currentExec . completed = true ; self . _execute ( err ) ; } ) ; break ; case 'forEachLateBound' : if ( ! currentExec . payload || ! currentExec . payload . length ) { // Payload is blank // Goto next chain currentExec . completed = true ; redo = true ; break ; } var resolvedPayload = self . getPath ( self . _context , currentExec . payload ) ; if ( ! resolvedPayload ) { // Resolved payload is blank // Goto next chain currentExec . completed = true ; redo = true ; break ; } // Replace own exec array with actual type of payload now we know what it is {{{ if ( argy . isType ( resolvedPayload , 'array' ) ) { currentExec . type = 'forEachArray' ; } else if ( argy . isType ( resolvedPayload , 'object' ) ) { currentExec . type = 'forEachObject' ; } else { throw new Error ( 'Cannot perform forEach over unknown object type: ' + argy . getType ( resolvedPayload ) ) ; } currentExec . payload = resolvedPayload ; self . _structPointer -- ; // Force re-eval of this chain item now its been replace with its real (late-bound) type redo = true ; // }}} break ; case 'mapArray' : var output = new Array ( currentExec . payload . length ) ; self . runArray ( currentExec . payload . map ( function ( item , iter ) { self . _context . _item = item ; self . _context . _key = iter ; return function ( next ) { run ( self . _options . context , currentExec . callback , function ( err , value ) { if ( err ) return next ( err ) ; output [ iter ] = value ; next ( ) ; } , [ item , iter ] ) ; } ; } ) , self . _options . limit , function ( err ) { currentExec . completed = true ; self . _set ( currentExec . output , output ) ; self . _execute ( err ) ; } ) ; break ; case 'mapObject' : var output = { } ; self . runArray ( Object . keys ( currentExec . payload ) . map ( function ( key ) { return function ( next ) { self . _context . _item = currentExec . payload [ key ] ; self . _context . _key = key ; run ( self . _options . context , currentExec . callback , function ( err , value , outputKey ) { output [ outputKey || key ] = value ; next ( err ) ; } , [ currentExec . payload [ key ] , key ] ) ; } ; } ) , self . _options . limit , function ( err ) { currentExec . completed = true ; self . _set ( currentExec . output , output ) ; self . _execute ( err ) ; } ) ; break ; case 'mapLateBound' : if ( ! currentExec . payload || ! currentExec . payload . length ) { // Payload is blank // Goto next chain currentExec . completed = true ; redo = true ; break ; } var resolvedPayload = self . getPath ( self . _context , currentExec . payload ) ; if ( ! resolvedPayload ) { // Resolved payload is blank // Goto next chain currentExec . completed = true ; redo = true ; break ; } // Replace own exec array with actual type of payload now we know what it is {{{ if ( argy . isType ( resolvedPayload , 'array' ) ) { currentExec . type = 'mapArray' ; } else if ( argy . isType ( resolvedPayload , 'object' ) ) { currentExec . type = 'mapObject' ; } else { throw new Error ( 'Cannot perform map over unknown object type: ' + argy . getType ( resolvedPayload ) ) ; } currentExec . payload = resolvedPayload ; self . _structPointer -- ; // Force re-eval of this chain item now its been replace with its real (late-bound) type redo = true ; // }}} break ; case 'parallelArray' : case 'seriesArray' : self . runArray ( currentExec . payload . map ( function ( task ) { return function ( next ) { run ( self . _options . context , task , next ) ; } ; } ) , currentExec . type == 'parallelArray' ? self . _options . limit : 1 , function ( err ) { currentExec . completed = true ; self . _execute ( err ) ; } ) ; break ; case 'seriesObject' : case 'parallelObject' : self . runArray ( Object . keys ( currentExec . payload ) . map ( function ( key ) { return function ( next ) { run ( self . _options . context , currentExec . payload [ key ] , function ( err , value ) { self . _set ( key , value ) ; // Allocate returned value to context next ( err ) ; } ) ; } ; } ) , currentExec . type == 'parallelObject' ? self . _options . limit : 1 , function ( err ) { currentExec . completed = true ; self . _execute ( err ) ; } ) ; break ; case 'race' : var hasResult = false ; var hasError = false ; self . runArray ( currentExec . payload . map ( function ( task ) { return function ( next ) { run ( self . _options . context , task , function ( err , taskResult ) { if ( err ) { hasError = true next ( err , taskResult ) ; } else if ( ! hasResult && ! hasError && taskResult !== null && typeof taskResult !== 'undefined' ) { self . _set ( currentExec . id , taskResult ) ; // Allocate returned value to context hasResult = true ; next ( '!RACEDONE!' , taskResult ) ; // Force an error to stop the run() function } else { next ( err , taskResult ) ; } } ) ; } ; } ) , self . _options . limit , function ( err , val ) { currentExec . completed = true ; // Override race finish error as it was just to stop the race and not a real one if ( err == '!RACEDONE!' ) return self . _execute ( ) ; self . _execute ( err ) ; } ) ; break ; case 'deferArray' : currentExec . payload . forEach ( function ( task ) { self . _deferAdd ( null , task , currentExec ) ; } ) ; redo = true ; break ; case 'deferObject' : Object . keys ( currentExec . payload ) . forEach ( function ( key ) { self . _deferAdd ( key , currentExec . payload [ key ] , currentExec ) ; } ) ; redo = true ; break ; case 'await' : // Await can operate in two modes, either payload=[] (examine all) else (examine specific keys) if ( ! currentExec . payload . length ) { // Check all tasks are complete if ( self . _struct . slice ( 0 , self . _structPointer - 1 ) . every ( function ( stage ) { // Examine all items UP TO self one and check they are complete return stage . completed ; } ) ) { // All tasks up to self point are marked as completed if ( _deferTimeoutHandle ) clearTimeout ( _deferTimeoutHandle ) ; currentExec . completed = true ; redo = true ; } else { self . _structPointer -- ; // At least one task is outstanding - rewind to self stage so we repeat on next resolution } } else { // Check certain tasks are complete by key if ( currentExec . payload . every ( function ( dep ) { // Examine all named dependencies return ! ! self . _context [ dep ] ; } ) ) { // All are present if ( _deferTimeoutHandle ) clearTimeout ( _deferTimeoutHandle ) ; currentExec . completed = true ; redo = true ; } else { self . _structPointer -- ; // At least one dependency is outstanding - rewind to self stage so we repeat on next resolution } } break ; case 'limit' : // Set the options.limit variable self . _options . limit = currentExec . payload ; currentExec . completed = true ; redo = true ; // Move on to next action break ; case 'timeout' : // Set the timeout function or its timeout value if ( currentExec . delay === false ) { // Disable self . _options . timeout = false ; } else { // Set the callback if one was passed if ( currentExec . callback ) self . _options . timeoutHandler = currentExec . callback ; // Set the delay if one was passed if ( currentExec . delay ) self . _options . timeout = currentExec . delay ; } currentExec . completed = true ; redo = true ; // Move to next action break ; case 'context' : // Change the self._options.context object self . _options . context = currentExec . payload ? currentExec . payload : self . _context ; // Set context (if null use internal context) currentExec . completed = true ; redo = true ; // Move on to next action break ; case 'set' : // Set a hash of variables within context Object . keys ( currentExec . payload ) . forEach ( function ( key ) { self . _set ( key , currentExec . payload [ key ] ) ; } ) ; currentExec . completed = true ; redo = true ; // Move on to next action break ; case 'end' : // self should ALWAYS be the last item in the structure and indicates the final function call this . _finalize ( ) ; break ; default : if ( this . _plugins [ currentExec . type ] ) { // Is there a plugin that should manage this? this . _plugins [ currentExec . type ] . call ( this , currentExec ) ; } else { throw new Error ( 'Unknown async-chainable exec type: ' + currentExec . type ) ; } return ; } } while ( redo ) ; }
Internal function to execute the next pending queue item
This is usually called after the completion of every asyncChainable.run call
@access private
Internal function to execute the next pending queue item This is usually called after the completion of every asyncChainable . run call