Hi all
I'm having an issue with util.promisify, trying to apply it to an object method, as follow
const net = require('net')
const util = require('util')
async function start () {
try {
const server = net.createServer()
const listen = util.promisify(server.listen)
await listen(9123)
console.log('started')
} catch (error) {
console.error(error)
}
}
start()
output is
TypeError: Cannot read property '_handle' of undefined
at Server.listen (net.js:1383:12)
at internal/util.js:277:30
at new Promise (<anonymous>)
at internal/util.js:276:12
at start (/home/simone/Desktop/util-promisify.js:8:11)
at Object.<anonymous> (/home/simone/Desktop/util-promisify.js:15:1)
at Module._compile (internal/modules/cjs/loader.js:945:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:962:10)
at Module.load (internal/modules/cjs/loader.js:798:32)
at Function.Module._load (internal/modules/cjs/loader.js:711:12)
because in net.js:1383 there is
if (this._handle) {
while util.promisify do
original.call(this, ...args, (err, ...values) => {
so this at execution of listen function is no more its own instance, but a new context.
Reading the documentation, this is not reported, so I'm opening this issue - that I suppose to be a bug.
In my opinion, a possible solution could be adding an optional arg instance to util.promisify, like
util.promisify(original, [instance])
then, after checked that instance is an object and instance[original] exists, could be
original.call(instance || this, ...args, (err, ...values) => {
it could be called as
util.promisify(server.listen, server)
and works as aspected.
If you agree with this solution, I'd be glad to do that.
You can use Function.prototype.call() on the function returned by util.promisify().
await listen.call(server, 9123);
or better use events.once(emitter, name) for your example.
Regarding your proposed solution: That would mean that you would have to create a new promisified function for each function and for each instance. I think @lpinca's suggestion is generally preferrable over that.
Please note that I'm not asking for help to solve my problem, I already did by writing my own promisify function.
I'm reporting that, imho, util.promisify does not work as aspected and there is no information about that in the documentation.
util.promisifydoes not work as aspected.
Why not? You are promisifying a function on net.Server prototype and call that function without context. It's the same of doing:
const listen = server.listen;
listen(9123, callback);
I agree with @lpinca, it is working as expected, that is just how JavaScript works.
Please note that I'm not asking for help to solve my problem, I already did by writing my own
promisifyfunction.
You can do that, or you can just use bind if you really want to make your promisifed functions specific to instances:
util.promisify(server.listen.bind(server))
In case, I prefer this solution
const server = net.createServer()
const listen = util.promisify(server.listen.bind(server))
await listen(9123)
Don't you think this should be in the documentation?
That seems to be exactly what I suggested 馃憤
Submitting in the same time :)
Don't you think this should be in the documentation?
It is just how JavaScript works, but since it is apparently causing confusion, I would be okay with briefly mentioning this in the documentation.
Thank you
You'd probably want something like this.
const server = net.createServer()
server.listen(9123)
await events.once(server, 'listening')
Otherwise, your app might hang if the listening event never fires due to an error, e.g. something's already listening on the same port.
Most helpful comment
You can use
Function.prototype.call()on the function returned byutil.promisify().or better use
events.once(emitter, name)for your example.