EventEmitter в середине цепочки Promises

Я делаю то, что включает в себя последовательность последовательности child_process.spawn() по порядку (для выполнения некоторой настройки, а затем запустите фактическую мясную команду, которую заинтересовал вызывающий, затем выполните некоторую очистку).

Что-то вроде:

doAllTheThings()
  .then(function(exitStatus){
    // all the things were done
    // and we've returned the exitStatus of
    // a command in the middle of a chain
  });

Где doAllTheThings() есть что-то вроде:

function doAllTheThings() {
  runSetupCommand()
    .then(function(){
      return runInterestingCommand();
    })
    .then(function(exitStatus){
      return runTearDownCommand(exitStatus); // pass exitStatus along to return to caller
    });
}

Внутри я использую child_process.spawn(), который возвращает EventEmitter, и я эффективно возвращаю результат события close из runInterestingCommand() обратно вызывающему.

Теперь мне также нужно отправить события data из stdout и stderr в вызывающий, которые также из EventEmitters. Есть ли способ сделать эту работу с (Bluebird) Promises, или они просто мешают EventEmitters, которые испускают более одного события?

В идеале я бы хотел написать:

doAllTheThings()
  .on('stdout', function(data){
    // process a chunk of received stdout data
  })
  .on('stderr', function(data){
    // process a chunk of received stderr data
  })
  .then(function(exitStatus){
    // all the things were done
    // and we've returned the exitStatus of
    // a command in the middle of a chain
  });

Единственный способ, с помощью которого я могу работать над своей программой, - это переписать ее, чтобы удалить цепочку обещаний, и просто использовать необработанный EventEmitter внутри того, что обертывает установку/отключение, что-то вроде:

withTemporaryState(function(done){
  var cmd = runInterestingCommand();
  cmd.on('stdout', function(data){
    // process a chunk of received stdout data
  });
  cmd.on('stderr', function(data){
    // process a chunk of received stderr data
  });
  cmd.on('close', function(exitStatus){
    // process the exitStatus
    done();
  });
});

Но так как EventEmitters настолько распространены во всех Node.js, я не могу не думать, что я должен быть способен заставить их работать в цепочках Promise. Любые подсказки?

На самом деле, одна из причин, по которой я хочу использовать Bluebird, заключается в том, что я хочу использовать функции аннулирования, чтобы разрешить отмену текущей команды извне.

Ответ 1

Существует два подхода: один предоставляет синтаксис, который вы изначально запрашивали, а другой - делегаты.

function doAllTheThings(){
     var com = runInterestingCommand();
     var p = new Promise(function(resolve, reject){
         com.on("close", resolve);
         com.on("error", reject);
     });
     p.on = function(){ com.on.apply(com, arguments); return p; };
     return p;
}

Что позволит вам использовать ваш желаемый синтаксис:

doAllTheThings()
  .on('stdout', function(data){
    // process a chunk of received stdout data
  })
  .on('stderr', function(data){
    // process a chunk of received stderr data
  })
  .then(function(exitStatus){
    // all the things were done
    // and we've returned the exitStatus of
    // a command in the middle of a chain
  });

Однако IMO это несколько вводит в заблуждение, и может быть желательно передать делегатов в:

function doAllTheThings(onData, onErr){
     var com = runInterestingCommand();
     var p = new Promise(function(resolve, reject){
         com.on("close", resolve);
         com.on("error", reject);
     });
     com.on("stdout", onData).on("strerr", onErr);
     return p;
}

Что бы вы сделали:

doAllTheThings(function(data){
    // process a chunk of received stdout data
  }, function(data){
    // process a chunk of received stderr data
  })
  .then(function(exitStatus){
    // all the things were done
    // and we've returned the exitStatus of
    // a command in the middle of a chain
  });