Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Combine Promise and EventEmitter

I have create this "simple pattern" that works for combine Promise and EventEmitter (with nodejs).

But: I'm wondering if there is a better way score a goal?

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);

I was thinking like:

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

but I can't figure out how do that.

like image 949
Manuel Spigolon Avatar asked Feb 28 '18 10:02

Manuel Spigolon


2 Answers

Node.js has built a function for this: the require('events').once function! Here the PR.

It has been released with Node [v11.13] (https://nodejs.org/en/blog/release/v11.13.0/)

An example usage (from docs):

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();
like image 72
Manuel Spigolon Avatar answered Sep 20 '22 02:09

Manuel Spigolon


Personally I'm not sure how the accepted answer is related to the OP's question, anyway I think I do have found a rather simple (but maybe not very nice) way of accomplishing the specific doSomething(...).on(...).then(...) thing asked by the OP. Taking the OP's example code, we can just do something like the following:

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);

It works for my limited cases so far, and both the event and the promise can be chained without issues as far as I know. There might be problems for more complicated use-cases that I have not encountered yet, but it does serve the purpose of chaining doSomething(...).on(...).then(...) like the OP asked.

like image 44
hellopeach Avatar answered Sep 17 '22 02:09

hellopeach