index.js
function execute(state, task, keys, callback)
{
  var key
    , control
    , assignment   = 'ASSIGNMENT'

      // get list of commands left for this iteration
    , commandsLeft = (keys || task).length
    , command      = (keys || task).shift()

      // job id within given task
    , jobId        = commandsLeft
    ;

  // we're done here
  // wait for all the commands
  if (commandsLeft === 0)
  {
    // if it's last one proceed to callback
    if (Object.keys(state._currentTask.waiters).length === 0)
    {
      callback(null, state);  // eslint-disable-line callback-return
    }

    return;
  }

  if (keys)
  {
    key     = command;
    command = task[command];

    // consider anything but strings, functions and arrays as direct state modifiers
    // there is no one right way to merge arrays, so leave it for custom functions
    if (['array', 'function', 'string'].indexOf(typeOf(command)) == -1)
    {
      report('start', state, assignment);

      // do shallow merge, until we needed otherwise
      state[key] = typeOf(command) == 'object' ? extend(state[key], command) : command;
      report('store', state, assignment, key);
      report('output', state, assignment, command);
      execute(state, task, keys, callback);
      return;
    }
  }

  report('start', state, command);

  // run the command and get terminator reference
  // if it'd need to be stopped
  control = run(clone(command), state, function(err, data)
  {
    // clean up waiters
    delete state._currentTask.waiters[jobId];

    if (err)
    {
      report('error', state, command, err);
      cleanWaiters(state);
      return callback(err);
    }

    // modify state if requested
    if (key)
    {
      state[key] = data;
      report('store', state, command, key);
    }

    report('output', state, command, data);

    // if it's last one proceed to callback
    if (Object.keys(state._currentTask.waiters).length === 0)
    {
      callback(null, state); // eslint-disable-line callback-return
      return;
    }
  });

  // if no control object returned
  // means run task was prematurely terminated
  // do not proceed
  if (!control)
  {
    return;
  }

  // keep reference to command related elements
  state._currentTask.waiters[jobId] =
  {
    command    : command,
    callback   : control.callback,
    terminator : control.terminator
  };

  // proceed to the next command within task
  execute(state, task, keys, callback);
}
function execute(state, task, keys, callback)
{
  var key
    , control
    , assignment   = 'ASSIGNMENT'

      // get list of commands left for this iteration
    , commandsLeft = (keys || task).length
    , command      = (keys || task).shift()

      // job id within given task
    , jobId        = commandsLeft
    ;

  // we're done here
  // wait for all the commands
  if (commandsLeft === 0)
  {
    // if it's last one proceed to callback
    if (Object.keys(state._currentTask.waiters).length === 0)
    {
      callback(null, state);  // eslint-disable-line callback-return
    }

    return;
  }

  if (keys)
  {
    key     = command;
    command = task[command];

    // consider anything but strings, functions and arrays as direct state modifiers
    // there is no one right way to merge arrays, so leave it for custom functions
    if (['array', 'function', 'string'].indexOf(typeOf(command)) == -1)
    {
      report('start', state, assignment);

      // do shallow merge, until we needed otherwise
      state[key] = typeOf(command) == 'object' ? extend(state[key], command) : command;
      report('store', state, assignment, key);
      report('output', state, assignment, command);
      execute(state, task, keys, callback);
      return;
    }
  }

  report('start', state, command);

  // run the command and get terminator reference
  // if it'd need to be stopped
  control = run(clone(command), state, function(err, data)
  {
    // clean up waiters
    delete state._currentTask.waiters[jobId];

    if (err)
    {
      report('error', state, command, err);
      cleanWaiters(state);
      return callback(err);
    }

    // modify state if requested
    if (key)
    {
      state[key] = data;
      report('store', state, command, key);
    }

    report('output', state, command, data);

    // if it's last one proceed to callback
    if (Object.keys(state._currentTask.waiters).length === 0)
    {
      callback(null, state); // eslint-disable-line callback-return
      return;
    }
  });

  // if no control object returned
  // means run task was prematurely terminated
  // do not proceed
  if (!control)
  {
    return;
  }

  // keep reference to command related elements
  state._currentTask.waiters[jobId] =
  {
    command    : command,
    callback   : control.callback,
    terminator : control.terminator
  };

  // proceed to the next command within task
  execute(state, task, keys, callback);
}
function execute ( state , task , keys , callback ) { var key , control , assignment = 'ASSIGNMENT' // get list of commands left for this iteration , commandsLeft = ( keys || task ) . length , command = ( keys || task ) . shift ( ) // job id within given task , jobId = commandsLeft ; // we're done here // wait for all the commands if ( commandsLeft === 0 ) { // if it's last one proceed to callback if ( Object . keys ( state . _currentTask . waiters ) . length === 0 ) { callback ( null , state ) ; // eslint-disable-line callback-return } return ; } if ( keys ) { key = command ; command = task [ command ] ; // consider anything but strings, functions and arrays as direct state modifiers // there is no one right way to merge arrays, so leave it for custom functions if ( [ 'array' , 'function' , 'string' ] . indexOf ( typeOf ( command ) ) == - 1 ) { report ( 'start' , state , assignment ) ; // do shallow merge, until we needed otherwise state [ key ] = typeOf ( command ) == 'object' ? extend ( state [ key ] , command ) : command ; report ( 'store' , state , assignment , key ) ; report ( 'output' , state , assignment , command ) ; execute ( state , task , keys , callback ) ; return ; } } report ( 'start' , state , command ) ; // run the command and get terminator reference // if it'd need to be stopped control = run ( clone ( command ) , state , function ( err , data ) { // clean up waiters delete state . _currentTask . waiters [ jobId ] ; if ( err ) { report ( 'error' , state , command , err ) ; cleanWaiters ( state ) ; return callback ( err ) ; } // modify state if requested if ( key ) { state [ key ] = data ; report ( 'store' , state , command , key ) ; } report ( 'output' , state , command , data ) ; // if it's last one proceed to callback if ( Object . keys ( state . _currentTask . waiters ) . length === 0 ) { callback ( null , state ) ; // eslint-disable-line callback-return return ; } } ) ; // if no control object returned // means run task was prematurely terminated // do not proceed if ( ! control ) { return ; } // keep reference to command related elements state . _currentTask . waiters [ jobId ] = { command : command , callback : control . callback , terminator : control . terminator } ; // proceed to the next command within task execute ( state , task , keys , callback ) ; }
Executes provided task with state object context

@private
@param   {object} state - current state
@param   {array} task - list of commands to execute
@param   {array|undefined} keys - list of task names
@param   {function} callback - invoked after all commands have been executed
@returns {void}
Executes provided task with state object context