位于 Promise 链中间的 EventEmitter

Posted

技术标签:

【中文标题】位于 Promise 链中间的 EventEmitter【英文标题】:EventEmitter in the middle of a chain of Promises 【发布时间】:2014-10-02 16:36:33 【问题描述】:

我正在做一些涉及按顺序运行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()返回给调用者。

现在我还需要将来自 stdout 和 stderr 的 data 事件发送给同样来自 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();
  );
);

但是由于 EventEmitter 在整个 Node.js 中如此普遍,我不禁认为我应该能够使它们在 Promise 链中工作。有什么线索吗?

其实我想继续使用 Bluebird 的原因之一,是因为我想使用 Cancellation 功能,让正在运行的命令可以从外部取消。

【问题讨论】:

请查看 Bluebird API 文档中有关进度的文档以及如何处理该问题。 @BenjaminGruenbaum 文档说 API 已被弃用,不应使用,但它似乎也没有回答我提出的问题?我没有跟踪进度,我只是有多个事件被触发并且需要能够处理所有事件。 那里的解决方案(进度的替代方案)应该适合您的情况。 Promise 不是事件发射器 - 拥有一个事件发射器并使用 Promise 来跟踪完成情况。 我遇到了同样的问题,我找到了一篇关于这种模式的好博文:datchley.name/promise-patterns-anti-patterns。你可以向下滚动到中间的“Executing Promises in Series”。 【参考方案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
  );

【讨论】:

同意你的最后一点。感谢您的详细回答! @Benjamin Gruenbaum 在上一个示例中传递一个 EventEmitter 对象怎么样?而不是传递几个参数? 这里不存在多次触发错误事件的危险吗?这也会导致 promise 被多次拒绝,这是不可能的。

以上是关于位于 Promise 链中间的 EventEmitter的主要内容,如果未能解决你的问题,请参考以下文章

删除链表中间节点和a/b处的节点

2.3 删除链表的中间节点和a/b处的节点

JavaScript小技能:原型链的运作机制Promise链

从如何停掉 Promise 链说起

如何在没有“缩进金字塔”的情况下正确表达任意 Promise 链? [复制]

如何使用链调用 Promise?