【问题标题】:Combine Promise and EventEmitter结合 Promise 和 EventEmitter
【发布时间】:2018-08-08 05:33:07
【问题描述】:

我创建了这个“简单模式”,适用于组合 PromiseEventEmitter(与 nodejs)。

但是:我想知道是否有更好的进球方式?

const { EventEmitter } = require('events');
const fs = require('fs');

function doSomething(parameters) {
  const emitter = new EventEmitter();

  const promise = new Promise((resolve, reject) => {
    // DO DIRTY JOB
    fs.readdir(parameters.directory, (err, files) => {
      if (err) {
        reject(err);
        return;
      }
      files.forEach(file => emitter.emit('update-event', file));
      resolve(`I'm done: ${parameters.param} world`);
    });
  });
  return { promise, emitter };
}

const work = doSomething({ param: 'hello', directory: './' });
work.emitter.on('update-event', data => console.log(`Update ${data}`));
work.promise.then(console.log).catch(console.error);

我在想:

doSomething(...).on(...).then(...)

但我不知道该怎么做。

【问题讨论】:

  • FYI.. Node V10.0.0 已经具有返回承诺的fsPromises.readdir() 功能。
  • 您正在一次触发所有事件 - 您不需要EventEmitter。用一个数组来兑现你的诺言。
  • @Bergi 我认为他只是展示了一个简单的示例,而他的实际代码更复杂,它需要进度事件以及完成后解决承诺。我也想做类似doSomething(...).on(...).then(...) 的事情,但我认为没有简单或标准化的方法可以做到这一点——也许只能通过扩展默认的 Promise 类并结合 EventEmitter 类的各个方面。

标签: node.js ecmascript-6 promise eventemitter


【解决方案1】:

Node.js 为此构建了一个函数:require('events').once 函数!这里是PR

已随 Node [v11.13] (https://nodejs.org/en/blog/release/v11.13.0/) 发布

一个示例用法(来自文档):

const { once, EventEmitter } = require('events');
async function run() {
  const ee = new EventEmitter();
  process.nextTick(() => {
    ee.emit('myevent', 42);
  });
  const [value] = await once(ee, 'myevent');
  console.log(value); // 42

  const err = new Error('kaboom');
  process.nextTick(() => {
    ee.emit('error', err);
  });

  try {
    await once(ee, 'myevent');
  } catch (err) {
    console.log('error happened', err);
  }
}

run();

【讨论】:

    【解决方案2】:

    就我个人而言,我不确定接受的答案与 OP 的问题有何关系,无论如何我认为我确实找到了一种相当简单(但可能不是很好)的方法来完成 OP 提出的特定 doSomething(...).on(...).then(...) 事情.以 OP 的示例代码为例,我们可以执行以下操作:

    const { EventEmitter } = require('events');
    const fs = require('fs');
    
    function doSomething(parameters) {
      var resolves;
      var rejects;
      const emitter = new EventEmitter();
      const promise = new Promise((resolve, reject) => {
        resolves = resolve;
        rejects = reject;
      });
      promise.on = emitter.on;
      promise.emit = emitter.emit;
    
      // DO DIRTY JOB
      fs.readdir(parameters.directory, (err, files) => {
        if (err) {
          rejects(err);
          return;
        }
        files.forEach(file => promise.emit('update-event', file));
        resolves(`I'm done: ${parameters.param} world`);
      });
    
      return promise;
    }
    
    const work = doSomething({ param: 'hello', directory: './' });
    work.on('update-event', data => console.log(`Update ${data}`))
      .then(console.log)
      .catch(console.error);
    

    到目前为止,它适用于我的有限情况,据我所知,事件和承诺都可以毫无问题地链接起来。对于我还没有遇到过的更复杂的用例,可能会有问题,但它确实起到了像 OP 所要求的那样链接 doSomething(...).on(...).then(...) 的目的。

    【讨论】:

      【解决方案3】:

      不,您不应该将事件发射器和承诺组合在一个对象中。像您一样单独退回它们是可以的。

      当然,在您的特定示例中,根本没有理由使用事件发射器。无论如何,它都会在承诺实现时触发。简单得多:

      const fs = require('fs');
      function doSomething(parameters) {
        return new Promise((resolve, reject) => {
          // DO DIRTY JOB
          fs.readdir(parameters.directory, (err, files) => {
            if (err) reject(err);
            else resolve(Object.assign(files, parameters));
          });
        });
      }
      
      doSomething({ param: 'hello', directory: './' }).then(files => {
        for (const data of files) {
          console.log(`Update ${data}`)
        }
        return `I'm done: ${files.param} world`;
      }).then(console.log, console.error);
      

      【讨论】:

      • 也许这个例子不是很清楚,但是如果我必须在一个以EventEmitter 方式思考的类上创建一个包装模块。对于现实世界的例子:肮脏的工作是管理process.spawn 事件,但我会在更高级别公开承诺链
      • 是的,因为同时使用事件发射器和承诺是可以的。
      【解决方案4】:

      我建议这样做:

      import EventPromised from "event-promised";
      
      function doSomething(parameters) {
          return new EventPromised((resolve, reject, emit) => {
              fs.readdir(parameters.directory, (err, files) => {
                  if (err) {
                      reject(err);
                      return;
                  }
                  files.forEach(file => emit('update-event', file));
                  resolve(`I'm done: ${parameters.param} world`);
              });
          });
      }
      doSomething({ param: 'hello', directory: './' })
          .on('update-event', data => console.log(`Update ${data}`))
          .then(console.log)
          .catch(console.error);
      

      【讨论】:

      • 您好,Matthieu,欢迎来到 StackOverflow!请确保follow our rules regarding self-promotion,最重要的是在您的回答中透露您是该库的作者。还请使每个答案都与相应的问题相关,而不是发布完全不相关的示例(特别是因为对多个问题发布相同的答案被视为垃​​圾邮件)。请edit你的帖子解决这些问题,然后你会得到我的支持!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-10-02
      • 2016-01-06
      • 1970-01-01
      • 1970-01-01
      • 2017-04-29
      • 2013-04-13
      相关资源
      最近更新 更多