Node: Add context option to utils.promisify() for method binding

Created on 31 May 2017  路  13Comments  路  Source: nodejs/node

  • Version 8:
  • Platform OSX:

The new utils.promisify() will promisify methods, but they lose their this context. Many libs require their methods to be bound.

Here is an example I'm working on with the offical Stripe package.

  stripe.refunds.create(refundOptions, (err, refund) => {
    if (err) {
      // handle error..
      return;
    }
    console.log(refund);
  });

If I want to promisify this:

// currently have to do
const createRefund = util.promisify(stripe.refunds.create);
const refund = await createRefund.call(stripe.refunds, refundOptions);

// or
const createRefund = util.promisify(stripe.refunds.create).bind(stripe.refunds);
const refund = await createRefund(refundOptions);

Ideally I would do this:

const createRefund = util.promisify(stripe.refunds.create, stripe.refunds);
const refund = await createRefund(refundOptions);

I've been using the ES6-promisify package for a while now, and it has this option. The Bluebird promisify method also has an option for this.

feature request promises util

Most helpful comment

Hi, I don't think we should mess with contexts, the user can reassign the function to the object and context will work (promisify has tests for this).

o.a = util.promisify(o.b);
o.a(); // `this` is o

All 13 comments

@wesbos I believe all Stripe methods return promises if callback is not provided https://github.com/stripe/stripe-node#using-promises

But idea of context for promisify makes sense.

Oh - I didn't know that! Awesome.

But yes - I've needed this for many other libs that don't offer promises.

@wesbos how do you imagine this should work in the following case?

var o1 = {a: true};
var o2 = {a: false};
function foo(cb) { cb(null, this.a); }
o2.f = util.promisify(foo, o1);
o2.f().then(console.log);

The PR implementing this got negative response so I guess we should close both the PR and this issue. cc @nodejs/collaborators objections?

Hi, I don't think we should mess with contexts, the user can reassign the function to the object and context will work (promisify has tests for this).

o.a = util.promisify(o.b);
o.a(); // `this` is o

Closing due to lack of support, see also #13440.

You can promisify all object methods without object modification using util.promisify and ES6 Proxy.
Use it like this:
doAsync(fs).readFile('package.json', 'utf8').then(result => {...})
No extra variables and duplicated objects (WeakMap is used). Check this package: https://github.com/doasync/doasync

(Just a note, Proxies are cool but pretty slow.)

(Just a note, Proxies are cool but pretty slow.)

I think pify implementation will be slower, but we should test.

Hi, I don't think we should mess with contexts, the user can reassign the function to the object and context will work (promisify has tests for this).

o.a = util.promisify(o.b);
o.a(); // `this` is o

Be careful with this, doing something like:

fs.readFile = util.promisify(fs.readFile);

while other packages use fs.readFile... it will mess things up.

Applying the native way as described in https://github.com/doasync/doasync, I guess we could do this.

const func2 = util.promisify(func)
const ret = await func2.apply(context, [arg1, arg2, ...])

For future generations reading this:

let openEntry = util.promisify(zip.stream).bind(zip);

is side-effect free and still one line.

@roychri for posterity - we offer fs.promises for that :]

Was this page helpful?
0 / 5 - 0 ratings

Related issues

stevenvachon picture stevenvachon  路  3Comments

fanjunzhi picture fanjunzhi  路  3Comments

willnwhite picture willnwhite  路  3Comments

sandeepks1 picture sandeepks1  路  3Comments

dfahlander picture dfahlander  路  3Comments