async-kit
Version:
A simple and powerful async abstraction lib for easily writing Node.js code.
1,467 lines (1,105 loc) • 318 kB
JavaScript
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
/*
Async Kit
Copyright (c) 2014 - 2016 Cédric Ronvel
The MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
"use strict" ;
var async = require( './core.js' ) ;
module.exports = async ;
async.wrapper = require( './wrapper.js' ) ;
async.exit = require( './exit.js' ) ;
},{"./core.js":3,"./exit.js":4,"./wrapper.js":5}],2:[function(require,module,exports){
/*
Async Kit
Copyright (c) 2014 - 2016 Cédric Ronvel
The MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
"use strict" ;
/* global window */
if ( ! window.setImmediate )
{
window.setImmediate = function( callback ) { return setTimeout( callback , 0 ) ; } ;
}
// Load async.js, export it, and set isBrowser to true
module.exports = require( './core.js' ) ;
module.exports.wrapper = require( './wrapper.js' ) ;
module.exports.isBrowser = true ;
},{"./core.js":3,"./wrapper.js":5}],3:[function(require,module,exports){
/*
Async Kit
Copyright (c) 2014 - 2016 Cédric Ronvel
The MIT License (MIT)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
// Async flow
/*
TODO:
- this: in all callback and event, it would be nice to bind the current execContext as 'this',
so for example each jobs can access to results of others...
-> DONE for callback but not for event
- serialProgress() callback/event that is called for each element, but that respect sequence order
even in parallel mode
- config(): just set everything in one place?
- dependenciesTree(): jobs are resolved using a dependencies' tree, that give arguments to transmit
from one Async function to another
- async.Queue: job can be added after exec(), forever, until quit() is called (it needs some brainstorming here),
basicaly, some work should be done to move jobs from async.Plan to async.ExecContext
- caolan/async's: compose(), detect(), filter() ?
- Real async try/catch/finally, using node's Domain?
- exportProxy() export a proxy function, if you call the function twice (or more) with the same arguments,
the subsequent call will process immediately, replaying all callback immediately and returning,
all with the same value
TODO Promises:
- promise() returning a Promise
- Promise as a job item, or action function
TODO Doc:
- Jeu de piste/Organigramme en Markdown, de simples questions proposent des liens en réponse,
menant sur d'autres questions avec d'autres liens de réponses, etc... à la fin, on obtient
un code d'exemple qui sert de template à copier/coller.
*/
"use strict" ;
// Load modules dependencies
var NextGenEvents = require( 'nextgen-events' ) ;
var treeExtend = require( 'tree-kit/lib/extend.js' ) ;
var async = {} ;
module.exports = async ;
//////////////////////////
// Internal Async Error //
//////////////////////////
// Extend Error
async.AsyncError = function AsyncError( message )
{
Error.call( this ) ;
Error.captureStackTrace && Error.captureStackTrace( this , this.constructor ) ; // jshint ignore:line
this.message = message ;
} ;
async.AsyncError.prototype = Object.create( Error.prototype ) ;
async.AsyncError.prototype.constructor = async.AsyncError ;
//////////////////////////////////////////////////////
// Async Plan factory: create different Plan object //
//////////////////////////////////////////////////////
// Empty constructor, it is just there to support instanceof operator
async.Plan = function Plan()
{
throw new Error( "[async] Cannot create an async Plan object directly" ) ;
} ;
//async.Plan.prototype = Object.create( NextGenEvents.prototype ) ;
async.Plan.prototype.constructor = async.Plan ;
// Common properties for all instance of async.Plan
var planCommonProperties = {
// Configurable
parallelLimit: { value: 1 , writable: true , enumerable: true , configurable: true } ,
raceMode: { value: false , writable: true , enumerable: true , configurable: true } ,
waterfallMode: { value: false , writable: true , enumerable: true , configurable: true } ,
waterfallTransmitError: { value: false , writable: true , enumerable: true , configurable: true } ,
whileAction: { value: undefined , writable: true , enumerable: true , configurable: true } ,
whileActionBefore: { value: false , writable: true , enumerable: true , configurable: true } ,
errorsAreFatal: { value: true , writable: true , enumerable: true , configurable: true } ,
returnMapping1to1: { value: false , writable: true , enumerable: true , configurable: true } ,
// Not configurable
jobsData: { value: {} , writable: true , enumerable: true } ,
jobsKeys: { value: [] , writable: true , enumerable: true } ,
jobsUsing: { value: undefined , writable: true , enumerable: true } ,
jobsTimeout: { value: undefined , writable: true , enumerable: true } ,
returnLastJobOnly: { value: false , writable: true , enumerable: true } ,
defaultAggregate: { value: undefined , writable: true , enumerable: true } ,
returnAggregate: { value: false , writable: true , enumerable: true } ,
transmitAggregate: { value: false , writable: true , enumerable: true } ,
usingIsIterator: { value: false , writable: true , enumerable: true } ,
thenAction: { value: undefined , writable: true , enumerable: true } ,
catchAction: { value: undefined , writable: true , enumerable: true } ,
finallyAction: { value: undefined , writable: true , enumerable: true } ,
asyncEventNice: { value: -20 , writable: true , enumerable: true } ,
maxRetry: { value: 0 , writable: true , enumerable: true } ,
retryTimeout: { value: 0 , writable: true , enumerable: true } ,
retryMultiply: { value: 1 , writable: true , enumerable: true } ,
retryMaxTimeout: { value: Infinity , writable: true , enumerable: true } ,
execMappingMinInputs: { value: 0 , writable: true , enumerable: true } ,
execMappingMaxInputs: { value: 100 , writable: true , enumerable: true } ,
execMappingCallbacks: { value: [ 'finally' ] , writable: true , enumerable: true } ,
execMappingAggregateArg: { value: false , writable: true , enumerable: true } ,
execMappingMinArgs: { value: 0 , writable: true , enumerable: true } ,
execMappingMaxArgs: { value: 101 , writable: true , enumerable: true } ,
execMappingSignature: { value: '( [finallyCallback] )' , writable: true , enumerable: true } ,
locked: { value: false , writable: true , enumerable: true }
} ;
// Create an async.Plan flow, parallel limit is preset to 1 (series) as default, but customizable with .parallel()
async.do = function _do( jobsData )
{
var asyncPlan = Object.create( async.Plan.prototype , planCommonProperties ) ;
Object.defineProperties( asyncPlan , {
execInit: { value: execDoInit.bind( asyncPlan ) } ,
execNext: { value: execDoNext.bind( asyncPlan ) } ,
execCallback: { value: execDoCallback } ,
execLoopCallback: { value: execWhileCallback } ,
execFinal: { value: execDoFinal.bind( asyncPlan ) }
} ) ;
asyncPlan.do( jobsData ) ;
return asyncPlan ;
} ;
// Create an async parallel flow
async.parallel = function parallel( jobsData )
{
var asyncPlan = Object.create( async.Plan.prototype , planCommonProperties ) ;
Object.defineProperties( asyncPlan , {
parallelLimit: { value: Infinity , writable: true , enumerable: true } ,
execInit: { value: execDoInit.bind( asyncPlan ) } ,
execNext: { value: execDoNext.bind( asyncPlan ) } ,
execCallback: { value: execDoCallback } ,
execLoopCallback: { value: execWhileCallback } ,
execFinal: { value: execDoFinal.bind( asyncPlan ) }
} ) ;
asyncPlan.do( jobsData ) ;
return asyncPlan ;
} ;
// Create an async series flow
async.series = function series( jobsData )
{
var asyncPlan = Object.create( async.Plan.prototype , planCommonProperties ) ;
Object.defineProperties( asyncPlan , {
parallelLimit: { value: 1 , enumerable: true } ,
execInit: { value: execDoInit.bind( asyncPlan ) } ,
execNext: { value: execDoNext.bind( asyncPlan ) } ,
execCallback: { value: execDoCallback } ,
execLoopCallback: { value: execWhileCallback } ,
execFinal: { value: execDoFinal.bind( asyncPlan ) }
} ) ;
asyncPlan.do( jobsData ) ;
return asyncPlan ;
} ;
// Create an async parallel flow, and return the result of the first non-error
async.race = function race( jobsData )
{
var asyncPlan = Object.create( async.Plan.prototype , planCommonProperties ) ;
Object.defineProperties( asyncPlan , {
raceMode: { value: true , enumerable: true } ,
parallelLimit: { value: Infinity , writable: true , enumerable: true } ,
errorsAreFatal: { value: false , writable: true , enumerable: true } ,
execInit: { value: execDoInit.bind( asyncPlan ) } ,
execNext: { value: execDoNext.bind( asyncPlan ) } ,
execCallback: { value: execDoCallback } ,
execLoopCallback: { value: execWhileCallback } ,
execFinal: { value: execDoFinal.bind( asyncPlan ) }
} ) ;
// We only want the result of the first succeeding job
asyncPlan.returnLastJobOnly = true ;
asyncPlan.do( jobsData ) ;
return asyncPlan ;
} ;
// Create an async series flow, each job transmit its results to the next jobs
async.waterfall = function waterfall( jobsData )
{
var asyncPlan = Object.create( async.Plan.prototype , planCommonProperties ) ;
Object.defineProperties( asyncPlan , {
waterfallMode: { value: true , enumerable: true } ,
waterfallTransmitError: { value: false , writable: true , enumerable: true } ,
parallelLimit: { value: 1 , enumerable: true } ,
execInit: { value: execDoInit.bind( asyncPlan ) } ,
execNext: { value: execDoNext.bind( asyncPlan ) } ,
execCallback: { value: execDoCallback } ,
execLoopCallback: { value: execWhileCallback } ,
execFinal: { value: execDoFinal.bind( asyncPlan ) }
} ) ;
// We only want the result of the first succeeding job
asyncPlan.returnLastJobOnly = true ;
asyncPlan.do( jobsData ) ;
return asyncPlan ;
} ;
// Create an async foreach, parallel limit is preset to 1 (series) as default, but customizable with .parallel()
async.foreach = async.forEach = function foreach( jobsData , iterator )
{
var asyncPlan = Object.create( async.Plan.prototype , planCommonProperties ) ;
Object.defineProperties( asyncPlan , {
usingIsIterator: { value: true , writable: true , enumerable: true } ,
errorsAreFatal: { value: false , writable: true , enumerable: true } ,
execInit: { value: execDoInit.bind( asyncPlan ) } ,
execNext: { value: execDoNext.bind( asyncPlan ) } ,
execCallback: { value: execDoCallback } ,
execLoopCallback: { value: execWhileCallback } ,
execFinal: { value: execDoFinal.bind( asyncPlan ) }
} ) ;
asyncPlan.do( jobsData ) ;
asyncPlan.iterator( iterator ) ;
return asyncPlan ;
} ;
// Create an async map, parallel limit is preset to Infinity, but customizable with .parallel()
async.map = function map( jobsData , iterator )
{
var asyncPlan = Object.create( async.Plan.prototype , planCommonProperties ) ;
Object.defineProperties( asyncPlan , {
parallelLimit: { value: Infinity , writable: true , enumerable: true } ,
usingIsIterator: { value: true , writable: true , enumerable: true } ,
errorsAreFatal: { value: false , writable: true , enumerable: true } ,
// the result mapping should match the jobs' data 1:1
returnMapping1to1: { value: true , writable: false , enumerable: true } ,
execInit: { value: execDoInit.bind( asyncPlan ) } ,
execNext: { value: execDoNext.bind( asyncPlan ) } ,
execCallback: { value: execDoCallback } ,
execLoopCallback: { value: execWhileCallback } ,
execFinal: { value: execDoFinal.bind( asyncPlan ) }
} ) ;
asyncPlan.do( jobsData ) ;
asyncPlan.iterator( iterator ) ;
return asyncPlan ;
} ;
// Create an async reduce, force parallel limit to 1 (does it make sense to do it in parallel?)
async.reduce = function reduce( jobsData , defaultAggregate , iterator )
{
var asyncPlan = Object.create( async.Plan.prototype , planCommonProperties ) ;
Object.defineProperties( asyncPlan , {
parallelLimit: { value: 1 , writable: false , enumerable: true } ,
usingIsIterator: { value: true , writable: true , enumerable: true } ,
execInit: { value: execDoInit.bind( asyncPlan ) } ,
execNext: { value: execDoNext.bind( asyncPlan ) } ,
execCallback: { value: execDoCallback } ,
execLoopCallback: { value: execWhileCallback } ,
execFinal: { value: execDoFinal.bind( asyncPlan ) }
} ) ;
if ( arguments.length < 3 )
{
// No defaultAggregate given
iterator = defaultAggregate ;
defaultAggregate = undefined ;
// Force exec signature to have an aggregateArg
asyncPlan.execMappingMinInputs = 0 ;
asyncPlan.execMappingMaxInputs = 100 ;
asyncPlan.execMappingCallbacks = [ 'finally' ] ;
asyncPlan.execMappingAggregateArg = true ;
asyncPlan.execMappingMinArgs = 1 ;
asyncPlan.execMappingMaxArgs = 102 ;
asyncPlan.execMappingSignature = '( aggregateArg, [finallyCallback] )' ;
}
asyncPlan.transmitAggregate = true ;
asyncPlan.returnAggregate = true ;
asyncPlan.defaultAggregate = defaultAggregate ;
asyncPlan.do( jobsData ) ;
asyncPlan.iterator( iterator ) ;
return asyncPlan ;
} ;
// async while
// Here, simple callback is mandatory, since it should process its inputs correctly in order to loop or not
async.while = function _while( whileAction )
{
var asyncPlan = Object.create( async.Plan.prototype , planCommonProperties ) ;
Object.defineProperties( asyncPlan , {
waterfallMode: { value: false , enumerable: true } ,
whileAction: { value: undefined , writable: true , enumerable: true } ,
whileActionBefore: { value: true , writable: false , enumerable: true } ,
execInit: { value: execDoInit.bind( asyncPlan ) } ,
execNext: { value: execDoNext.bind( asyncPlan ) } ,
execCallback: { value: execDoCallback } ,
execLoopCallback: { value: execWhileCallback } ,
execFinal: { value: execDoFinal.bind( asyncPlan ) }
} ) ;
asyncPlan.while( whileAction ) ;
return asyncPlan ;
} ;
// Create an async AND
async.and = function and( jobsData )
{
var asyncPlan = Object.create( async.Plan.prototype , planCommonProperties ) ;
Object.defineProperties( asyncPlan , {
elseAction: { value: undefined , writable: true , enumerable: true } ,
castToBoolean: { value: false , writable: true , enumerable: true } ,
useLogicAnd: { value: true } ,
execInit: { value: execDoInit.bind( asyncPlan ) } ,
execNext: { value: execDoNext.bind( asyncPlan ) } ,
execCallback: { value: execLogicCallback } ,
execLoopCallback: { value: execWhileCallback } ,
execFinal: { value: execLogicFinal.bind( asyncPlan ) }
} ) ;
asyncPlan.do( jobsData ) ;
return asyncPlan ;
} ;
// Create an async OR (it's close to AND)
async.or = function or( jobsData )
{
var asyncPlan = Object.create( async.Plan.prototype , planCommonProperties ) ;
Object.defineProperties( asyncPlan , {
elseAction: { value: undefined , writable: true , enumerable: true } ,
castToBoolean: { value: false , writable: true , enumerable: true } ,
useLogicAnd: { value: false } ,
execInit: { value: execDoInit.bind( asyncPlan ) } ,
execNext: { value: execDoNext.bind( asyncPlan ) } ,
execCallback: { value: execLogicCallback } ,
execLoopCallback: { value: execWhileCallback } ,
execFinal: { value: execLogicFinal.bind( asyncPlan ) }
} ) ;
asyncPlan.do( jobsData ) ;
return asyncPlan ;
} ;
// Syntaxic sugar: various if notations
async.if = function _if( jobsData )
{
var asyncPlan = Object.create( async.Plan.prototype , planCommonProperties ) ;
Object.defineProperties( asyncPlan , {
elseAction: { value: true , writable: true , enumerable: true } ,
castToBoolean: { value: true , writable: true , enumerable: true } ,
useLogicAnd: { value: true } ,
execInit: { value: execDoInit.bind( asyncPlan ) } ,
execNext: { value: execDoNext.bind( asyncPlan ) } ,
execCallback: { value: execLogicCallback } ,
execLoopCallback: { value: execWhileCallback } ,
execFinal: { value: execLogicFinal.bind( asyncPlan ) }
} ) ;
if ( jobsData ) { asyncPlan.do( jobsData ) ; }
return asyncPlan ;
} ;
async.if.and = async.if ;
async.if.or = function ifOr( jobsData )
{
var asyncPlan = Object.create( async.Plan.prototype , planCommonProperties ) ;
Object.defineProperties( asyncPlan , {
elseAction: { value: true , writable: true , enumerable: true } ,
castToBoolean: { value: true , writable: true , enumerable: true } ,
useLogicAnd: { value: false } ,
execInit: { value: execDoInit.bind( asyncPlan ) } ,
execNext: { value: execDoNext.bind( asyncPlan ) } ,
execCallback: { value: execLogicCallback } ,
execLoopCallback: { value: execWhileCallback } ,
execFinal: { value: execLogicFinal.bind( asyncPlan ) }
} ) ;
if ( jobsData ) { asyncPlan.do( jobsData ) ; }
return asyncPlan ;
} ;
////////////////
// Shorthands //
////////////////
// Accept only one function, timeout it
// async.timeout( fn , timeout , [maxRetry] , [retryTimeout] , [multiply] , [maxRetryTimeout] )
//async.timeout = function timeout( func , timeoutValue , maxRetry , retryTimeout , multiply , maxRetryTimeout )
async.callTimeout = function callTimeout( timeout , completionCallback , fn , this_ )
{
if ( typeof fn !== 'function' ) { throw new Error( '[async] async.callTimeout(): argument #0 should be a function' ) ; }
var asyncPlan = Object.create( async.Plan.prototype , planCommonProperties ) ;
Object.defineProperties( asyncPlan , {
returnLastJobOnly: { value: true , enumerable: true } ,
jobsTimeout: { value: timeout , writable: true , enumerable: true } ,
execInit: { value: execDoInit.bind( asyncPlan ) } ,
execNext: { value: execDoNext.bind( asyncPlan ) } ,
execCallback: { value: execDoCallback } ,
execLoopCallback: { value: execWhileCallback } ,
execFinal: { value: execDoFinal.bind( asyncPlan ) }
} ) ;
var job = [ fn.bind( this_ ) ].concat( Array.prototype.slice.call( arguments , 4 ) ) ;
asyncPlan.do( [ job ] ) ;
//if ( arguments.length > 2 ) { asyncPlan.retry( maxRetry , retryTimeout , multiply , maxRetryTimeout ) ; }
return asyncPlan.exec( completionCallback ) ;
} ;
///////////////////////
// Async Plan object //
///////////////////////
// Set the job's list
async.Plan.prototype.do = function _do( jobsData )
{
if ( this.locked ) { return this ; }
if ( jobsData && typeof jobsData === 'object' ) { this.jobsData = jobsData ; }
else if ( typeof jobsData === 'function' ) { this.jobsData = [ jobsData ] ; this.returnLastJobOnly = true ; }
else { this.jobsData = {} ; }
this.jobsKeys = Object.keys( this.jobsData ) ;
return this ;
} ;
// Set number of jobs running in parallel
async.Plan.prototype.parallel = function parallel( parallelLimit )
{
if ( this.locked ) { return this ; }
if ( parallelLimit === undefined || parallelLimit === true ) { this.parallelLimit = Infinity ; }
else if ( parallelLimit === false ) { this.parallelLimit = 1 ; }
else if ( typeof parallelLimit === 'number' ) { this.parallelLimit = parallelLimit ; }
return this ;
} ;
// Set race mode: we stop processing jobs when the first non-error job finish.
// Notice: when using race() this way (without the async.race() factory), you have to call .fatal( false ) too if you want the same behaviour.
async.Plan.prototype.race = function race( raceMode )
{
if ( ! this.locked ) { this.raceMode = raceMode || raceMode === undefined ? true : false ; }
return this ;
} ;
// Set waterfall mode: each job pass its results to the next job.
// Be careful, this does not support parallel mode ATM, but may fail silently.
// TODO: should probably raise an exception if we use parallel mode.
async.Plan.prototype.waterfall = function waterfall( waterfallMode )
{
if ( ! this.locked ) { this.waterfallMode = waterfallMode || waterfallMode === undefined ? true : false ; }
return this ;
} ;
// Set while action.
// Here, simple callback is mandatory, since it should process its inputs correctly in order to loop or not.
// If whileActionBefore is given and truthy, then it makes a do( jobs ).while( callback , true ) the same as while( whileAction ).do( jobs ):
// the while condition is evaluated before any jobs are processed.
// Write this form only for non-trivial uses.
async.Plan.prototype.while = function _while( whileAction , whileActionBefore )
{
if ( this.locked ) { return this ; }
this.whileAction = whileAction ;
if ( whileActionBefore !== undefined ) { this.whileActionBefore = whileActionBefore ? true : false ; }
return this ;
} ;
// Set the number of time to repeat the action.
// It is the same as while(), provided with a simple counter function.
async.Plan.prototype.repeat = function repeat( n )
{
if ( this.locked ) { return this ; }
var i = 0 ;
if ( typeof n !== 'number' ) { n = parseInt( n ) ; }
this.whileActionBefore = true ;
this.whileAction = function( error , results , callback ) {
// callback should be called last, to avoid sync vs async mess, hence i++ come first, and we check i<=n rather than i<n
i ++ ;
callback( i <= n ) ;
} ;
return this ;
} ;
// Set if errors are fatal or not
async.Plan.prototype.fatal = function fatal( errorsAreFatal )
{
if ( ! this.locked ) { this.errorsAreFatal = errorsAreFatal || errorsAreFatal === undefined ? true : false ; }
return this ;
} ;
// Cast logic jobs to boolean
async.Plan.prototype.boolean = function boolean( castToBoolean )
{
if ( ! this.locked ) { this.castToBoolean = castToBoolean || castToBoolean === undefined ? true : false ; }
return this ;
} ;
// Transmit error, in waterfall mode
async.Plan.prototype.transmitError = function transmitError( waterfallTransmitError )
{
if ( ! this.locked ) { this.waterfallTransmitError = waterfallTransmitError || waterfallTransmitError === undefined ? true : false ; }
return this ;
} ;
// Set the timeout for each jobs, the callback will be called with an async error for each of them that timeout
async.Plan.prototype.timeout = function timeout( jobsTimeout )
{
if ( ! this.locked )
{
if ( typeof jobsTimeout === 'number' ) { this.jobsTimeout = jobsTimeout ; }
else { this.jobsTimeout = undefined ; }
}
return this ;
} ;
// Set how to retry jobs in error
async.Plan.prototype.retry = function retry( maxRetry , timeout , multiply , maxTimeout )
{
if ( this.locked ) { return this ; }
if ( typeof maxRetry === 'number' ) { this.maxRetry = maxRetry ; }
if ( typeof timeout === 'number' ) { this.retryTimeout = timeout ; }
if ( typeof multiply === 'number' ) { this.retryMultiply = multiply ; }
if ( typeof maxTimeout === 'number' ) { this.retryMaxTimeout = maxTimeout ; }
return this ;
} ;
// Set if only the last job's results should be passed to the callback
async.Plan.prototype.lastJobOnly = function lastJobOnly( returnLastJobOnly )
{
if ( ! this.locked ) { this.returnLastJobOnly = returnLastJobOnly || returnLastJobOnly === undefined ? true : false ; }
return this ;
} ;
// Set if the result mapping should match the jobs' data 1:1
async.Plan.prototype.mapping1to1 = function mapping1to1( returnMapping1to1 )
{
if ( ! this.locked ) { this.returnMapping1to1 = returnMapping1to1 || returnMapping1to1 === undefined ? true : false ; }
return this ;
} ;
// Set the performer of the jobs: if set, do() is not feeded by callback but by arguments for this single callback function.
// The performer function should accept a callback as its last argument, in the nodejs' way.
async.Plan.prototype.using = function using( jobsUsing )
{
if ( ! this.locked ) { this.jobsUsing = jobsUsing ; }
return this ;
} ;
// Same as using(), but the given function receive an uniq "element" containing the whole job as its first argument:
// it is like using().usingIterator(), a behaviour similar to the async.foreach() factory
async.Plan.prototype.iterator = function iterator( iterator_ )
{
if ( this.locked ) { return this ; }
this.jobsUsing = iterator_ ;
this.usingIsIterator = true ;
return this ;
} ;
// Transmit aggregate, for aggregator mode (reduce, etc)
async.Plan.prototype.aggregator = function aggregator( transmitAggregate , returnAggregate , defaultAggregate )
{
if ( ! this.locked ) { return this ; }
this.transmitAggregate = transmitAggregate || transmitAggregate === undefined ? true : false ;
this.returnAggregate = returnAggregate || returnAggregate === undefined ? true : false ;
if ( arguments.length > 2 ) { this.defaultAggregate = defaultAggregate ; }
return this ;
} ;
// Set if using() is an iterator (like async.foreach()), if so, the whole job is transmitted as one argument rather than an argument list
// NODOC
async.Plan.prototype.usingIterator = function usingIterator( usingIsIterator )
{
if ( ! this.locked ) { this.usingIsIterator = usingIsIterator || usingIsIterator === undefined ? true : false ; }
return this ;
} ;
// Set the async'ness of the flow, even sync jobs can be turned async
async.Plan.prototype.nice = function nice( asyncEventNice )
{
if ( this.locked ) { return this ; }
if ( asyncEventNice === undefined || asyncEventNice === null || asyncEventNice === true ) { this.asyncEventNice = -1 ; }
else if ( asyncEventNice === false ) { this.asyncEventNice = -20 ; }
else { this.asyncEventNice = asyncEventNice ; }
return this ;
} ;
// Set action to do on completion.
// If catch() or else() are present and match, then() is not triggered.
async.Plan.prototype.then = function then( thenAction )
{
if ( ! this.locked ) { this.thenAction = thenAction ; }
return this ;
} ;
// Set action to do on logical false status
async.Plan.prototype.else = function _else( elseAction )
{
if ( ! this.locked ) { this.elseAction = elseAction || true ; }
return this ;
} ;
// Set action to do on error
async.Plan.prototype.catch = function _catch( catchAction )
{
if ( ! this.locked ) { this.catchAction = catchAction || true ; }
return this ;
} ;
// Set action to do, that trigger whether it has triggered or not any of then()/catch()/else()
async.Plan.prototype.finally = function _finally( finallyAction )
{
if ( ! this.locked ) { this.finallyAction = finallyAction || true ; }
return this ;
} ;
// Return a clone of the object
async.Plan.prototype.clone = function clone()
{
var asyncPlan = Object.create( async.Plan.prototype , planCommonProperties ) ;
treeExtend( null , asyncPlan , this ) ;
asyncPlan.locked = false ;
return asyncPlan ;
} ;
// Export the async.Plan object as an async function, so it can be called later at will
async.Plan.prototype.export = function _export( execMethod )
{
switch ( execMethod )
{
case 'execFinally' :
return this.clone().execFinally.bind( this ) ;
case 'execThenCatch' :
return this.clone().execThenCatch.bind( this ) ;
case 'execThenElse' :
return this.clone().execThenElse.bind( this ) ;
case 'execThenElseCatch' :
return this.clone().execThenElseCatch.bind( this ) ;
case 'execArgs' :
return this.clone().execArgs.bind( this ) ;
case 'execKV' :
return this.clone().execKV.bind( this ) ;
default :
return this.clone().exec.bind( this ) ;
}
} ;
// This is the common exec() function, its arguments can be mapped using execMapping()
async.Plan.prototype.exec = function exec()
{
var config = { inputs: [] , callbacks: {} } , offset = 0 , i ;
if ( arguments.length < this.execMappingMinArgs )
{
throw new Error( "[async] Too few arguments, in this instance, the function signature is: fn" + this.execMappingSignature ) ;
}
else if ( arguments.length > this.execMappingMaxArgs )
{
throw new Error( "[async] Too much arguments, in this instance, the function signature is: fn" + this.execMappingSignature ) ;
}
if ( this.execMappingAggregateArg )
{
offset ++ ;
config.aggregate = arguments[ 0 ] ;
}
if ( this.execMappingMinInputs === this.execMappingMaxInputs )
{
// Fixed arguments count, variable callback count possible
config.inputs = Array.prototype.slice.call( arguments , offset , this.execMappingMaxInputs + offset ) ;
for ( i = 0 ; i < this.execMappingCallbacks.length && config.inputs.length + i < arguments.length ; i ++ )
{
config.callbacks[ this.execMappingCallbacks[ i ] ] = arguments[ config.inputs.length + offset + i ] ;
}
}
else
{
// Variable arguments count, fixed callback count
config.inputs = Array.prototype.slice.call( arguments , offset , - this.execMappingCallbacks.length ) ;
for ( i = 0 ; i < this.execMappingCallbacks.length ; i ++ )
{
config.callbacks[ this.execMappingCallbacks[ i ] ] = arguments[ config.inputs.length + offset + i ] ;
}
}
return this.execInit( config ) ;
} ;
// Exec templates
async.Plan.prototype.execFinally = function execFinally( finallyCallback )
{
return this.execInit( { inputs: [] , callbacks: { 'finally': finallyCallback } } ) ;
} ;
async.Plan.prototype.execThenCatch = function execThenCatch( thenCallback , catchCallback , finallyCallback )
{
return this.execInit( { inputs: [] , callbacks: { 'then': thenCallback , 'catch': catchCallback , 'finally': finallyCallback } } ) ;
} ;
async.Plan.prototype.execThenElse = function execThenElse( thenCallback , elseCallback , finallyCallback )
{
return this.execInit( { inputs: [] , callbacks: { 'then': thenCallback , 'else': elseCallback , 'finally': finallyCallback } } ) ;
} ;
async.Plan.prototype.execThenElseCatch = function execThenElseCatch( thenCallback , elseCallback , catchCallback , finallyCallback )
{
return this.execInit( { inputs: [] , callbacks: { 'then': thenCallback , 'else': elseCallback , 'catch': catchCallback , 'finally': finallyCallback } } ) ;
} ;
async.Plan.prototype.execArgs = function execArgs()
{
return this.execInit( { inputs: arguments , callbacks: {} } ) ;
} ;
// Configure the inputs of exec() function
// .callbacks
// .minInputs
// .maxInputs
// .inputsName
// .aggregateArg
async.Plan.prototype.execMapping = function execMapping( config )
{
if ( this.locked ) { return this ; }
config = treeExtend( null , { minInputs: 0 , maxInputs: 0 } , config ) ;
var i , j , maxUnnamed = 5 ;
config.minInputs = parseInt( config.minInputs ) ;
config.maxInputs = parseInt( config.maxInputs ) ;
if ( config.minInputs < config.maxInputs )
{
this.execMappingMinInputs = config.minInputs ;
this.execMappingMaxInputs = config.maxInputs ;
}
else
{
// User is stOopid, swap...
this.execMappingMinInputs = config.maxInputs ;
this.execMappingMaxInputs = config.minInputs ;
}
this.execMappingCallbacks = Array.isArray( config.callbacks ) ? config.callbacks : [] ;
this.execMappingInputsName = Array.isArray( config.inputsName ) ? config.inputsName : [] ;
this.execMappingSignature = '( ' ;
if ( this.execMappingMinInputs === this.execMappingMaxInputs )
{
// Fixed input count, variable callback count possible
this.execMappingMinArgs = this.execMappingMinInputs ;
this.execMappingMaxArgs = this.execMappingMaxInputs + this.execMappingCallbacks.length ;
if ( config.aggregateArg )
{
this.execMappingAggregateArg = config.aggregateArg ;
this.execMappingMinArgs ++ ;
this.execMappingMaxArgs ++ ;
this.execMappingSignature += 'aggregateValue' ;
}
for ( i = 0 ; i < this.execMappingMaxInputs ; i ++ )
{
if ( i > 0 || config.aggregateArg ) { this.execMappingSignature += ', ' ; }
if ( i >= maxUnnamed && typeof this.execMappingInputsName[ i ] !== 'string' ) { this.execMappingSignature += '... ' ; break ; }
this.execMappingSignature += typeof this.execMappingInputsName[ i ] === 'string' ? this.execMappingInputsName[ i ] : 'arg#' + ( i + 1 ) ;
}
for ( j = 0 ; j < this.execMappingCallbacks.length ; j ++ )
{
if ( i + j > 0 || config.aggregateArg ) { this.execMappingSignature += ', ' ; }
this.execMappingSignature += '[' + this.execMappingCallbacks[ j ] + 'Callback]' ;
}
}
else
{
// Variable input count, fixed callback count
this.execMappingMinArgs = this.execMappingMinInputs + this.execMappingCallbacks.length ;
this.execMappingMaxArgs = this.execMappingMaxInputs + this.execMappingCallbacks.length ;
if ( config.aggregateArg )
{
this.execMappingAggregateArg = config.aggregateArg ;
this.execMappingMinArgs ++ ;
this.execMappingMaxArgs ++ ;
this.execMappingSignature += 'aggregateValue' ;
}
for ( i = 0 ; i < this.execMappingMaxInputs ; i ++ )
{
if ( i > 0 || config.aggregateArg ) { this.execMappingSignature += ', ' ; }
if ( i < this.execMappingMinInputs )
{
if ( i >= maxUnnamed && typeof this.execMappingInputsName[ i ] !== 'string' ) { this.execMappingSignature += '... ' ; break ; }
this.execMappingSignature += typeof this.execMappingInputsName[ i ] === 'string' ? this.execMappingInputsName[ i ] : 'arg#' + ( i + 1 ) ;
}
else
{
if ( i >= maxUnnamed && typeof this.execMappingInputsName[ i ] !== 'string' ) { this.execMappingSignature += '[...] ' ; break ; }
this.execMappingSignature += '[' + ( typeof this.execMappingInputsName[ i ] === 'string' ? this.execMappingInputsName[ i ] : 'arg#' + ( i + 1 ) ) + ']' ;
}
}
for ( j = 0 ; j < this.execMappingCallbacks.length ; j ++ )
{
if ( i + j > 0 || config.aggregateArg ) { this.execMappingSignature += ', ' ; }
this.execMappingSignature += this.execMappingCallbacks[ j ] + 'Callback' ;
}
}
this.execMappingSignature += ' )' ;
return this ;
} ;
// More sage and deterministic exec(), with all arguments given into a single object
async.Plan.prototype.execKV = function execKV( config )
{
if ( config.inputs === undefined ) { config.inputs = [] ; }
else if ( ! Array.isArray( config.inputs ) ) { config.inputs = [ config.inputs ] ; }
if ( config.callbacks === undefined || typeof config.callbacks !== 'object' ) { config.callbacks = {} ; }
if ( config.then ) { config.callbacks.then = config.then ; }
if ( config.else ) { config.callbacks.else = config.else ; }
if ( config.catch ) { config.callbacks.catch = config.catch ; }
if ( config.finally ) { config.callbacks.finally = config.finally ; }
// Nothing to do here, user is free to pass whatever is needed
//if ( config.aggregate === undefined ) { config.aggregate = null ; }
return this.execInit( config ) ;
} ;
// Internal, what to do on new loop iteration
async.Plan.prototype.execLoop = function execLoop( fromExecContext ) { return this.execInit( {} , fromExecContext ) ; } ;
// Internal exec of callback-like job/action
async.Plan.prototype.execJob = function execJob( execContext , job , indexOfKey , tryIndex )
{
var self = this , args , key = execContext.jobsKeys[ indexOfKey ] ;
// Create the job's context
var jobContext = Object.create( async.JobContext.prototype , {
execContext: { value: execContext , enumerable: true } ,
indexOfKey: { value: indexOfKey , enumerable: true } ,
tryIndex: { value: tryIndex , enumerable: true } ,
aborted: { value: false , writable: true , enumerable: true } ,
abortedLoop: { value: false , writable: true , enumerable: true }
} ) ;
// Add the callback to the context
Object.defineProperty( jobContext , 'callback' , {
value: this.execCallback.bind( this , jobContext ) ,
enumerable: true
} ) ;
// Also add the jobContext into the bounded function: it's an alternate way to access a job's context.
Object.defineProperty( jobContext.callback , 'jobContext' , {
value: jobContext ,
enumerable: true
} ) ;
// Set the current job's status to 'pending'
execContext.jobsStatus[ key ].status = 'pending' ;
execContext.jobsStatus[ key ].tried ++ ;
// Set up the nice value? For instance only syncEmit() are used
//jobContext.setNice( this.asyncEventNice ) ;
if ( typeof this.jobsUsing === 'function' )
{
if ( this.usingIsIterator )
{
if ( this.transmitAggregate )
{
if ( this.jobsUsing.length <= 3 )
{
this.jobsUsing.call( jobContext , execContext.aggregate , job , jobContext.callback ) ;
}
else if ( this.jobsUsing.length <= 4 )
{
this.jobsUsing.call( jobContext , execContext.aggregate , job , Array.isArray( execContext.jobsData ) ? indexOfKey : key , jobContext.callback ) ;
}
else
{
this.jobsUsing.call( jobContext , execContext.aggregate , job , Array.isArray( execContext.jobsData ) ? indexOfKey : key , execContext.jobsData , jobContext.callback ) ;
}
}
else
{
if ( this.jobsUsing.length <= 2 )
{
this.jobsUsing.call( jobContext , job , jobContext.callback ) ;
}
else if ( this.jobsUsing.length <= 3 )
{
this.jobsUsing.call( jobContext , job , Array.isArray( execContext.jobsData ) ? indexOfKey : key , jobContext.callback ) ;
}
else
{
this.jobsUsing.call( jobContext , job , Array.isArray( execContext.jobsData ) ? indexOfKey : key , execContext.jobsData , jobContext.callback ) ;
}
}
}
else if ( Array.isArray( job ) )
{
args = job.slice() ;
if ( this.transmitAggregate ) { args.unshift( execContext.aggregate ) ; }
args.push( jobContext.callback ) ;
this.jobsUsing.apply( jobContext , args ) ;
}
else
{
this.jobsUsing.call( jobContext , job , jobContext.callback ) ;
}
}
else if ( typeof job === 'function' )
{
if ( this.waterfallMode && indexOfKey > 0 )
{
// remove the first, error arg if waterfallTransmitError is false
//console.log( index , key , execContext.results ) ;
args = execContext.results[ execContext.jobsKeys[ indexOfKey - 1 ] ].slice( this.waterfallTransmitError ? 0 : 1 ) ;
args.push( jobContext.callback ) ;
job.apply( jobContext , args ) ;
}
else if ( Array.isArray( this.jobsUsing ) || this.execMappingMaxInputs )
{
if ( Array.isArray( this.jobsUsing ) ) { args = treeExtend( null , [] , this.jobsUsing , execContext.execInputs ) ; }
else { args = treeExtend( null , [] , execContext.execInputs ) ; }
args.push( jobContext.callback ) ;
job.apply( jobContext , args ) ;
}
else
{
job.call( jobContext , jobContext.callback ) ;
}
}
else if ( Array.isArray( job ) && typeof job[ 0 ] === 'function' )
{
args = job.slice( 1 ) ;
args.push( jobContext.callback ) ;
job[ 0 ].apply( jobContext , args ) ;
}
else if ( typeof job === 'object' && job instanceof async.Plan )
{
// What to do with jobUsing and execContext.execInputs here? Same as if( typeof job === 'function' ) ?
job.exec( jobContext.callback ) ;
}
else
{
this.execCallback.call( this , jobContext ) ;
return this ;
}
// Timers management
if ( execContext.jobsTimeoutTimers[ key ] !== undefined )
{
clearTimeout( execContext.jobsTimeoutTimers[ key ] ) ;
execContext.jobsTimeoutTimers[ key ] = undefined ;
}
if ( execContext.retriesTimers[ key ] !== undefined )
{
clearTimeout( execContext.retriesTimers[ key ] ) ;
execContext.retriesTimers[ key ] = undefined ;
}
if ( typeof this.jobsTimeout === 'number' && this.jobsTimeout !== Infinity )
{
execContext.jobsTimeoutTimers[ key ] = setTimeout( function() {
execContext.jobsTimeoutTimers[ key ] = undefined ;
execContext.jobsStatus[ key ].status = 'timeout' ;
jobContext.emit( 'timeout' ) ;
self.execCallback.call( self , jobContext , new async.AsyncError( 'jobTimeout' ) ) ;
} , this.jobsTimeout ) ;
}
return this ;
} ;
// Internal exec of callback-like action
async.Plan.prototype.execAction = function execAction( execContext , action , args )
{
// call the matching action
if ( typeof action === 'function' )
{
action.apply( execContext , args ) ;
}
else if ( typeof action === 'object' && action instanceof async.Plan )
{
action.exec() ;
}
} ;
/////////////////////////////////////////////////////////////////////////
// Async JobContext: Context of a job execution, transmitted as *this* //
/////////////////////////////////////////////////////////////////////////
// Empty constructor, it is just there to support instanceof operator
async.JobContext = function JobContext()
{
throw new Error( "[async] Cannot create an async JobContext object directly" ) ;
} ;
// Extends it from EventEmitter
async.JobContext.prototype = Object.create( NextGenEvents.prototype ) ;
async.JobContext.prototype.constructor = async.JobContext ;
// Permit a userland-side abort of the job's queue
async.JobContext.prototype.abort = function abort()
{
this.aborted = true ;
this.callback.apply( undefined , arguments ) ;
} ;
// Permit a userland-side abort of the job's queue, and event the whole loop
async.JobContext.prototype.abortLoop = function abortLoop()
{
this.aborted = true ;
this.abortedLoop = true ;
this.callback.apply( undefined , arguments ) ;
} ;
//////////////////////////////////////////////////
// Async ExecContext: Context of plan execution //
//////////////////////////////////////////////////
// Empty constructor, it is just there to support instanceof operator
async.ExecContext = function ExecContext()
{
throw new Error( "[async] Cannot create an async ExecContext object directly" ) ;
} ;
// Extends it from EventEmitter
async.ExecContext.prototype = Object.create( NextGenEvents.prototype ) ;
async.ExecContext.prototype.constructor = async.ExecContext ;
// This is used to complete jobsStatus only on-demand, so big data that are not object (e.g. big string)
// does not get duplicated for nothing
async.ExecContext.prototype.getJobsStatus = function getJobsStatus()
{
var i , key , fullJobsStatus = Array.isArray( this.jobsData ) ? [] : {} ;
for ( i = 0 ; i < this.jobsKeys.length ; i ++ )
{
key = this.jobsKeys[ i ] ;
fullJobsStatus[ key ] = treeExtend( null , {
job: this.jobsData[ key ] ,
result: this.results[ key ]
} ,
this.jobsStatus[ key ]
) ;
}
return fullJobsStatus ;
} ;
function execDoInit( config , fromExecContext )
{
var i , isArray = Array.isArray( this.jobsData ) ;
// Create instanceof ExecContext
var execContext = Object.create( async.ExecContext.prototype , {
plan: { value: this } ,
aggregate: { value: ( 'aggregate' in config ? config.aggregate : this.defaultAggregate ) , writable: true , enumerable: true } ,
results: { value: ( isArray ? [] : {} ) , writable: true , enumerable: true } ,
result: { value: undefined , writable: true , enumerable: true } , // Conditionnal version
jobsTimeoutTimers: { value: ( isArray ? [] : {} ) , writable: true } ,
jobsStatus: { value: ( isArray ? [] : {} ) , writable: true , enumerable: true } ,
retriesTimers: { value: ( isArray ? [] : {} ) , writable: true } ,
retriesCounter: { value: ( isArray ? [] : {} ) , writable: true , enumerable: true } ,
tryUserResponseCounter: { value: ( isArray ? [] : {} ) , writable: true , enumerable: true } ,
tryResponseCounter: { value: ( isArray ? [] : {} ) , writable: true , enumerable: true } ,
iterator: { value: 0 , writable: true , enumerable: true } ,
pending: { value: 0 , writable: true , enumerable: true } ,
resolved: { value: 0 , writable: true , enumerable: true } ,
ok: { value: 0 , writable: true , enumerable: true } ,
failed: { value: 0 , writable: true , enumerable: true } ,
status: { value: undefined , writable: true , enumerable: true } ,
error: { value: undefined , writable: true , enumerable: true } ,
statusTriggerJobsKey: { value: undefined , writable: true , enumerable: true } ,
whileStatus: { value: undefined , writable: true } ,
// true if current execContext has looped in another execContext (one loop per execContext possible)
// false if this execContext will never loop, undefined if this isn't settled
whileChecked: { value: false , writable: true }
} ) ;
// Add some properties depending on inherited ExecContext or not
if ( ! fromExecContext )
{
// This is the top-level/first ExecContext
Object.defineProperties( execContext , {
root: { value: execContext , enumerable: true } ,
jobsData: {
value: ( isArray ? this.jobsData.slice(0) : treeExtend( null , {} , this.jobsData ) ) ,
enumerable: true
} ,
jobsKeys: { value: this.jobsKeys.slice(0) , enumerable: true } ,
execInputs: { value: config.inputs , enumerable: true } ,
execCallbacks: { value: config.callbacks } ,
whileIterator: { value: 0 , enumerable: true , writable: true }
} ) ;
}
else
{
// This is a loop, and this ExecContext is derived from the first one
Object.defineProperties( execContext , {
root: { value: fromExecContext.root , enumerable: true } ,
jobsData: { value: fromExecContext.jobsData , enumerable: true } ,
jobsKeys: { value: fromExecContext.jobsKeys , enumerable: true } ,
execInputs: { value: fromExecContext.execInputs , enumerable: true } ,
execCallbacks: { value: fromExecContext.execCallbacks } ,
whileIterator: { value: fromExecContext.whileIterator + 1 , enumerable: true , writable: true }
} ) ;
}
// Add more properties depending on previous properties
Object.defineProperties( execContext , {
waiting: { value: execContext.jobsKeys.length , writable: true , enumerable: true }
} ) ;
// Init the jobsStatus
for ( i = 0 ; i < execContext.jobsKeys.length ; i ++ )
{
execContext.jobsStatus[ execContext.jobsKeys[ i ] ] = {
status: 'waiting' ,
errors: [] ,
tried: 0
} ;
}
// Set up the nice value
execContext.setNice( this.asyncEventNice ) ;
// Initialize event listeners, only the first time
if ( fromExecContext === undefined )
{
// Register execFinal to the 'resolved' event
execContext.root.on( 'resolved' , this.execFinal.bind( this , execContext ) ) ;
// Register whileAction to the 'while' event and exec to the 'nextLoop' event
// Here, simple callback is mandatory
if ( typeof this.whileAction === 'function' )
{
execContext.root.on( 'while' , this.whileAction.bind( this ) ) ;
execContext.root.on( 'nextLoop' , this.execLoop.bind( this ) ) ;
}
else
{
this.whileAction = undefined ; // falsy value: do not trigger while code
execContext.whileStatus = false ; // settle while status to false
}
// Register execNext to the next event
execContext.root.on( 'next' , this.execNext.bind( this ) ) ;
// If we are in a async.while().do() scheme, start whileAction before doing anything
if ( this.whileAction && this.whileActionBefore )
{
execContext.whileIterator = -1 ;
execContext.root.emit( 'while' , execContext.error , execContext.results , this.execLoopCallback.bind( this , execContext ) , null ) ;
return this ;
}
}
// If no jobs are provided, then exit right now
if ( execContext.jobsKeys.length <= 0 )
{
execContext.root.emit( 'resolved' , execContext.error , execContext.results ) ;
execContext.root.emit( 'progress' , {
resolved: exe