Search code examples
javascriptnode.jspdfpromiseeventemitter

How return event emitter in node.js


I have this piece of code

module.exports = function(file, callback) {
  var output, pdf, result, stderr;
  output = '';
  stderr = '';
  result = [];
  pdf = child_process.spawn('pdftotext', ['-layout', '-enc', 'UTF-8', file.name, '-']);
  pdf.stdout.setEncoding('utf8');
  pdf.stderr.setEncoding('utf8');
  pdf.stdout.on('data', function(data) {
    if (data) {
      output += data;
    }
  });
  pdf.stderr.on('data', function(data) {
    if (data) {
      stderr += data;
    }
  });
  return pdf.on('close', function(code) {
    var last_page, pages;
    if (code !== 0) {
      return stderr;
    } else {
      pages = output.split(/\f/);
      if (!pages) {
        return 'Nenhum texto foi encontrado';
      }
      last_page = pages[pages.length - 1];
      if (!last_page) {
        pages.pop();
      }
      file.text += pages;
      return callback(file);
    }
  });
};

Basically I'm taking the text of PDFs using the pdftotext bash program.

This function is being called inside a Promise.map, so I need to return a new array of files, but before return, I need to call the callback, each will be responsible for other things.

The problem is: how can I return the pdf.on('close', funct....) ??

Inside it, I have the return callback(file); each is returning exactly what I want, but now I need return the same thing from pdf.on....

Thanks.


Solution

  • Your function, being asynchronous, should return a promise. Scrap that callback.

    module.exports = function(filename) {
      var output = '';
      var stderr = '';
      var pdf = child_process.spawn('pdftotext', ['-layout', '-enc', 'UTF-8', filename, '-']);
      pdf.stdout.setEncoding('utf8');
      pdf.stderr.setEncoding('utf8');
      pdf.stdout.on('data', function(data) {
        if (data) output += data;
      });
      pdf.stderr.on('data', function(data) {
        if (data) stderr += data;
      });
      return new Promise(function(resolve, reject) {
    //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        pdf.on('close', function(code) {
          if (code)
            reject(new Error(stderr, code)); // call reject() for errors…
          else
            resolve(output); // and resolve() with the result *instead of that `callback`*
        });
      }).then(function(result) { // so that you then can chain processing to
        var pages = result.split(/\f/);
        if (!pages.length) {
          throw new Error('Nenhum texto foi encontrado');
        }
        var last_page = pages[pages.length - 1];
        if (!last_page)
          pages.pop();
        return pages.join("");
      });
    };
    

    With that, you can easily use it as a proper Promise.map callback:

    function phase_one(files) {
      return Promise.map(files, function(file) {
        return pdf(file.name).then(function(text) {
          file.text = text;
          return file;
        });
      }, {
        concurrency: 3000
      });
    }