util: add util.promisify() · nodejs/node@99da8e8
@@ -4,6 +4,8 @@ const errors = require('internal/errors');
44const binding = process.binding('util');
55const signals = process.binding('constants').os.signals;
667+const { createPromise, promiseResolve, promiseReject } = binding;
8+79const kArrowMessagePrivateSymbolIndex = binding['arrow_message_private_symbol'];
810const kDecoratedPrivateSymbolIndex = binding['decorated_private_symbol'];
911const noCrypto = !process.versions.openssl;
@@ -217,3 +219,62 @@ module.exports = exports = {
217219// default isEncoding implementation, just in case userland overrides it.
218220kIsEncodingSymbol: Symbol('node.isEncoding')
219221};
222+223+const kCustomPromisifiedSymbol = Symbol('util.promisify.custom');
224+const kCustomPromisifyArgsSymbol = Symbol('customPromisifyArgs');
225+226+function promisify(orig) {
227+if (typeof orig !== 'function') {
228+const errors = require('internal/errors');
229+throw new errors.TypeError('ERR_INVALID_ARG_TYPE', 'original', 'function');
230+}
231+232+if (orig[kCustomPromisifiedSymbol]) {
233+const fn = orig[kCustomPromisifiedSymbol];
234+if (typeof fn !== 'function') {
235+throw new TypeError('The [util.promisify.custom] property must be ' +
236+'a function');
237+}
238+Object.defineProperty(fn, kCustomPromisifiedSymbol, {
239+value: fn, enumerable: false, writable: false, configurable: true
240+});
241+return fn;
242+}
243+244+// Names to create an object from in case the callback receives multiple
245+// arguments, e.g. ['stdout', 'stderr'] for child_process.exec.
246+const argumentNames = orig[kCustomPromisifyArgsSymbol];
247+248+function fn(...args) {
249+const promise = createPromise();
250+try {
251+orig.call(this, ...args, (err, ...values) => {
252+if (err) {
253+promiseReject(promise, err);
254+} else if (argumentNames !== undefined && values.length > 1) {
255+const obj = {};
256+for (var i = 0; i < argumentNames.length; i++)
257+obj[argumentNames[i]] = values[i];
258+promiseResolve(promise, obj);
259+} else {
260+promiseResolve(promise, values[0]);
261+}
262+});
263+} catch (err) {
264+promiseReject(promise, err);
265+}
266+return promise;
267+}
268+269+Object.setPrototypeOf(fn, Object.getPrototypeOf(orig));
270+271+Object.defineProperty(fn, kCustomPromisifiedSymbol, {
272+value: fn, enumerable: false, writable: false, configurable: true
273+});
274+return Object.defineProperties(fn, Object.getOwnPropertyDescriptors(orig));
275+}
276+277+promisify.custom = kCustomPromisifiedSymbol;
278+279+exports.promisify = promisify;
280+exports.customPromisifyArgs = kCustomPromisifyArgsSymbol;