diff --git a/doc/api/deprecations.md b/doc/api/deprecations.md index 7cabc90b278fd8..8b9c9e59adc7b5 100644 --- a/doc/api/deprecations.md +++ b/doc/api/deprecations.md @@ -3387,12 +3387,15 @@ Consider using alternatives such as the [`mock`][] helper function. -Type: Documentation-only +Type: Runtime Calling [`util.promisify`][] on a function that returns a will ignore the result of said promise, which can lead to unhandled promise rejections. diff --git a/lib/internal/util.js b/lib/internal/util.js index 3586084ba7b8bd..3d63b983a2cbed 100644 --- a/lib/internal/util.js +++ b/lib/internal/util.js @@ -63,7 +63,7 @@ const { sleep: _sleep, toUSVString: _toUSVString, } = internalBinding('util'); -const { isNativeError } = internalBinding('types'); +const { isNativeError, isPromise } = internalBinding('types'); const { getOptionValue } = require('internal/options'); const noCrypto = !process.versions.openssl; @@ -409,7 +409,10 @@ function promisify(original) { resolve(values[0]); } }); - ReflectApply(original, this, args); + if (isPromise(ReflectApply(original, this, args))) { + process.emitWarning('Calling promisify on a function that returns a Promise is likely a mistake.', + 'DeprecationWarning', 'DEP0174'); + } }); } diff --git a/test/parallel/test-util-promisify.js b/test/parallel/test-util-promisify.js index 0fc2d650ee9272..e2318a4973b972 100644 --- a/test/parallel/test-util-promisify.js +++ b/test/parallel/test-util-promisify.js @@ -7,6 +7,29 @@ const vm = require('vm'); const { promisify } = require('util'); const { customPromisifyArgs } = require('internal/util'); +{ + const warningHandler = common.mustNotCall(); + process.on('warning', warningHandler); + function foo() {} + foo.constructor = (async () => {}).constructor; + promisify(foo); + process.off('warning', warningHandler); +} + +common.expectWarning( + 'DeprecationWarning', + 'Calling promisify on a function that returns a Promise is likely a mistake.', + 'DEP0174'); +promisify(async (callback) => { callback(); })().then(common.mustCall(() => { + // We must add the second `expectWarning` call in the `.then` handler, when + // the first warning has already been triggered. + common.expectWarning( + 'DeprecationWarning', + 'Calling promisify on a function that returns a Promise is likely a mistake.', + 'DEP0174'); + promisify(async () => {})().then(common.mustNotCall()); +})); + const stat = promisify(fs.stat); {