docs: function onCommand(cmd, args) {
  
    if(cmd === Constants.MAP.multi.name) {
      return this._abort(new Error('multi transaction cannot be nested'));
    }
  
    if(cmd === Constants.MAP.watch.name) {
      return this._abort(
        new Error('watch should be executed before a multi transaction'));
    }
  
    if(!this._client || !Array.isArray(this._queue)) {
      throw new Error('multi transaction already aborted');
    }
  
    function doExec(cmd, args, cb) {
      this._client.execute(cmd, args, cb);
    }
  
    doExec = doExec.bind(this);
  
    function onComplete(cmd, args, err, reply) {
  
      if(typeof args[0] === 'function') {
        args[0](err, reply);
      }else{
        this._client.emit('exec', err, reply);
      }
  
      // reset, this multi can be re-used
      this._reset();
    }
  
    function onQueue(cmd, args, err, reply) {
      this._queue.push(err || reply);
  
      var next = this._commands.shift();
      /* istanbul ignore else */
      if(next) {
        // executing a command
        if(next.cmd !== Constants.MAP.exec.name) {
          doExec(next.cmd, next.args, onQueue.bind(this, next.cmd, next.args));
        // running the exec command to get the result
        }else{
          doExec(next.cmd, [], onComplete.bind(this, cmd, next.args));
        }
      }
    }
  
    // send first multi packet
    if(!this._multi) {
      doExec(
        this._commands[0].cmd,
        this._commands[0].args,
        onQueue.bind(
          this,
          this._commands[0].cmd, []));
  
      // clear multi packet
      this._commands = [];
      this._multi = true;
    }
  
    // stash the command in a queue
    this._commands.push({cmd: cmd, args: args});
  }