1
\$\begingroup\$

I have a software that does the following in sequence:

download text files from s3
reads the files
collect the data from files

The program is asynchronous-synchronous - each phase is asynchronous, but it heavily depends on the output of the previous phase, so I'm using Promises for build the chaining.

The problem of this architecture is that is using to much CPU since I'm spawning child processes in the extract and collect phase. So I decide to change to events:

How I'm doing:

I have 3 eventEmitters: downloadFile, extract, collect:

downloadfile.emit("copy", s3file);
downloadfile.on("error", callback);
downloadfile.on("close", (file) => {
 extract.emit("file", file);
 extract.on("error", callback);
 extract.on("close", (file) => {
   collect.emit("data", file.data);
   collect.on("error", callback);
   collect.on("close", (data) => {
      data is here... i can use without waiting all the other files.
   });
 });
});

But this isn't very readable... I prefer the Promise way:

Promise.resolve(options)
  .then(downloadFiles)
  .then(extract)
  .then(collect);

So I was thinking... is it possible to chain the events? I mean, the output of an event will be the input of other, just like the Promises? If so, how could I achieve that?

\$\endgroup\$
  • \$\begingroup\$ What does emit actually do here? Are downloadfile, extract and collect global entities, or per-sequence? \$\endgroup\$ – Joseph Mar 28 '17 at 14:17
  • \$\begingroup\$ Per sequence, downloadfile -> extract -> collect \$\endgroup\$ – celoxxx Mar 28 '17 at 14:30
1
\$\begingroup\$

You should note that there's a significant difference between events and Promises. Events listen and fire multiple times. However, Promises are a one-time deal. Once it resolves/rejects, the callback won't fire again. You'll have to recreate the whole sequence for another series. With that:

is it possible to chain the events?

Yes and no. It depends on the event source. In your case, assuming downloadfile, extract and collect are event sources for a single operations, you can simply wrap that event source in a promise that resolves/rejects.

If so, how could i achieve that?

Assuming emit is a trigger to start the operation, error as the error listener and close as success, you can simply wrap them in promises, where error calls rejectand close calls resolve.

function downloadFiles(downloader, options){
  return new Promise((resolve, reject) => {
    downloader.emit('copy', file);
    downloader.on('error', reject);
    downloader.on('close', resolve);
  });
}

function extract(extractor, file){
  return new Promise((resolve, reject) => {
    extractor.emit('copy', file);
    extractor.on('error', reject);
    extractor.on('close', resolve);
  });
}

function collect(collector, data){
  return new Promise((resolve, reject) => {
    collector.emit('copy', data);
    collector.on('error', reject);
    collector.on('close', resolve);
  });
}

function createSequence(){
  var downloader = createDownloadFileThingy();
  var extractor = createExtractThingy();
  var collector = createCollectThingy();

  return Promise.resolve(options)
    .then(options => downloadFiles(downloader, options))
    .then(file => extract(extractor, file))
    .then(file => collect(collector, file.data));
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.