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.
@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 :]
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).