Bluebird: Promise.promisify fails on fs.createReadStream

Created on 5 Jun 2014  路  5Comments  路  Source: petkaantonov/bluebird

Hi,

I just wanted to checkout this library and played a little bit with the pomisify stuff but it does not work for all methods:

var Promise = require('bluebird');
var createReadStream = Promise.promisify(require('fs').createReadStream);


var p1 = createReadStream('app.js').then(console.log);

setTimeout(function() {
    console.log(p1);
}, 1000);

The p1 gets never fulfilled, no error, no success. I did test it for createWriteStream and it did not work, either.

Most helpful comment

Something to note here is that createReadStream can fail in the future, and even if you created the stream inside a promise chain the error will be reported uncatched (and your application die). This is quite unfortunate, for example in the case of trying to open a missing file, you get a stream and in the next tick you get an exception.
You need to create a stream like this to avoid problems, although this is far from ideal:

function createReadStream(filename){
  return new Promise(function(resolve, reject){
    function onError(err){
      reject(err);
    }

    function onReadable(){
      cleanup();
      resolve(stream);
    }

    function cleanup(){
      stream.removeListener('readable', onReadable);
      stream.removeListener('error', onError);
    }

    var stream = fs.createReadStream(filename);
    stream.on('error', onError);
    stream.on('readable', function(){
      resolve(stream);
    });
  });
}

@petkaantonov have you give any thought about a proper way to make streams work well together with promises?

All 5 comments

createReadStream is not a callback taking function, from the docs:

Returns a function that will wrap the given nodeFunction. Instead of taking a callback, the returned function will return a promise whose fate is decided by the callback behavior of the given node function. The node function should conform to node.js convention of accepting a callback as last argument and calling that callback with error as the first argument and success value on the second argument.

There is no need to promisify createReadStream since it's a synchronous function, you can just call it normally and get the return value immediately back.

Oh gosh, you are right. It happened because I used promisifyAll (createReadStreamAsync), my bad. Sry 8-)

Something to note here is that createReadStream can fail in the future, and even if you created the stream inside a promise chain the error will be reported uncatched (and your application die). This is quite unfortunate, for example in the case of trying to open a missing file, you get a stream and in the next tick you get an exception.
You need to create a stream like this to avoid problems, although this is far from ideal:

function createReadStream(filename){
  return new Promise(function(resolve, reject){
    function onError(err){
      reject(err);
    }

    function onReadable(){
      cleanup();
      resolve(stream);
    }

    function cleanup(){
      stream.removeListener('readable', onReadable);
      stream.removeListener('error', onError);
    }

    var stream = fs.createReadStream(filename);
    stream.on('error', onError);
    stream.on('readable', function(){
      resolve(stream);
    });
  });
}

@petkaantonov have you give any thought about a proper way to make streams work well together with promises?

Your comment is really useful, thanks @manast !!

Regret digging up an old/closed thread, but I found @manast 's example useful and informative.

Hoping this will help anyone else who stumbles upon it: (tiny correction at the end)

function createReadStream(filename){
  return new Promise(function(resolve, reject){
    function onError(err){
      reject(err);
    }

    function onReadable(){
      cleanup();
      resolve(stream);
    }

    function cleanup(){
      stream.removeListener('error', onError);
      stream.removeListener('readable', onReadable);
    }

    var stream = fs.createReadStream(filename);
    stream.on('error', onError);
    stream.on('readable', onReadable);    // <-- small correction
  });
}

Expanded for the Writable case:

function createWriteStream(filename){
  return new Promise(function(resolve, reject){
    function onError(err){
      reject(err);
    }

    function onOpen(){
      cleanup();
      resolve(stream);
    }

    function cleanup(){
      stream.removeListener('error', onError);
      stream.removeListener('open', onOpen);
    }

    const stream = fs.createWriteStream(filename);
    stream.on('error', onError);
    stream.on('open', onOpen);
  });
}
Was this page helpful?
0 / 5 - 0 ratings