Qiulang
Qiulang

Reputation: 12405

bluebird Promise.promisifyAll can work while promisify a function of that object fails

I try to promisify SFTPWrapper and hit this problem. I am not sure if it happens to other object or not.

So if I just promisify one SFTPWrapper function, say readdir, bluebird will have unhandledRejection : "Cannot read property 'readdir' of undefined" error. I tried util.promisify, the same error too.

But if promisifyAll(SFTPWrapper) and it work as expected. But why is that ?

---- update -----

The code I use,

var Client = require('ssh2').Client
var conn = new Client()
conn.on('ready', function() {
    conn.sftp(async function(err, sftp) {
        if (err) throw err
        try {
          // promisify will have Cannot read property 'readdir' of undefined error
          // both bluebird and util have the error

          //let readdirAsync = Promise.promisify(sftp.readdir)
          let readdirAsync = util.promisify(sftp.readdir)
          list = await readdirAsync(remotePathToList)

         // Promise.promisifyAll(sftp) work
         const sftp2 = Promise.promisifyAll(sftp)
         let list = await sftp2.readdirAsync(toRead)

Upvotes: 0

Views: 579

Answers (1)

jfriend00
jfriend00

Reputation: 707436

You don't show exactly where that error "Cannot read property 'readdir' of undefined error" occurs. If it occurs on this line of code:

let readdirAsync = util.promisify(sftp.readdir);

Then, the problem is that sftp is undefined. But, it doesn't appear that that is exactly the issue because you say that const sftp2 = Promise.promisifyAll(sftp) does work.

So, I'm guessing that the problem occurs when you try to use readdirAsync. If that's the case, then it's probably because you lost the sftp parent when you did let readdirAsync = util.promisify(sftp.readdir) and that is somehow important to the readdir method implementation. If that is indeed the case, then you can do this:

let readdirAsync = util.promisify(sftp.readdir).bind(sftp);

To make sure the parent object stays bound to the method. Because Bluebird's .promisifyAll() puts the new methods on the original object and you call them as methods on the object, not as plain functions, this binding to the original object happens automatically when you call them as sftp2.readdirAsync(). You could do that also as in:

sftp.readdirAsync = util.promisify(sftp.readdir);

And, then you could call sftp.readdirAsync(...).then(...).catch(...) and they'd be bound appropriately to the sftp object.

P.S. Using if (err) throw err inside an async callback is NEVER good error handling. You literally should never write that line of code inside an async callback unless the caller of that async callback has an explicit handler for that exception (which is usually not the case). All it does is throw an exception into async oblivion with no opportunity for actual error handling anywhere else in your code.

Upvotes: 2

Related Questions