util: add `options` to util.promisify()
This PR adds an optional second argument to util.promisify()
.
options.resolveArray
solves a problem with callbacks taking more than one non-error argument: Promise can't be resolved with more than one value, so further results are lost. With this option, Promise is resolved with an array of all non-error arguments.
For internal methods, this problem is solved with appropriate kCustomPromisifiedSymbol
and kCustomPromisifyArgsSymbol
properties. But util.promisify()
is a part of public API, and this approach is not applicable to userland.
options.callbackPosition
solves a problem with functions not having callback-last signature.
Most notably, a function can't have callback as last argument when we want to be able to omit last arguments or use ...rest
:
- We can't do
util.promisify((foo, optionalBar, cb) => cb(!foo, optionalBar))('fooValue', callbackFn)
and expectoptionalBar
to be magically undefined orarguments.length
to be magically adjusted. - We can't do
(foo, ...optionalArgs, cb) => {}
at all.
With that new option, we'll be able to promisify (foo, cb, optionalBar = 'defaultBar', ...restArgs) => {}
.
Example:
'use strict';
const { promisify } = require('node:util');
const fn = (foo, bar, baz, cb) => void cb(baz !== undefined, foo, bar, baz);
const fnOpt = (cb, ...args) => void cb(args[2] !== undefined, ...args);
// old
const Pfn = promisify(fn);
(async () => {
console.log(await Pfn(1, 2, undefined)); // 1
console.log(await Pfn(1, 2)); // TypeError: cb is not a function
})().catch(err => console.error('caught:', err));
// new
const PfnArr = promisify(fn, { resolveArray: true }); // callbackPosition: 3
const PfnOpt = promisify(fnOpt, { resolveArray: true, callbackPosition: 0 });
(async () => {
console.log(await PfnArr(1, 2, undefined)); // [ 1, 2, undefined ]
console.log(await PfnOpt(1, 2)); // [ 1, 2 ]
console.log(await PfnArr(1, 2, 3)); // caught: true (desired truthy error)
})().catch(err => console.error('caught:', err));
Documentation might require some rewording.