Browse Source

promise: better stack traces for --trace-warnings

Give better stack traces for `PromiseRejectionHandledWarning`
and `UnhandledPromiseRejectionWarning`s.

For `PromiseRejectionHandledWarning`, when it is likely that there
is an `Error` object generated, it is created early to provide a
proper stack trace.

For `UnhandledPromiseRejectionWarning`, the stack trace of the
underlying error object is used, if possible.

Fixes: https://github.com/nodejs/node/issues/9523
PR-URL: https://github.com/nodejs/node/pull/9525
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Reviewed-By: Daniel Bevenius <daniel.bevenius@gmail.com>
Reviewed-By: Michael Dawson <michael_dawson@ca.ibm.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Michaël Zasso <targos@protonmail.com>
v6
Anna Henningsen 8 years ago
parent
commit
196d27dd8c
No known key found for this signature in database GPG Key ID: D8B9F5AEAE84E4CF
  1. 17
      lib/internal/process/promises.js
  2. 5
      test/message/unhandled_promise_trace_warnings.js
  3. 31
      test/message/unhandled_promise_trace_warnings.out

17
lib/internal/process/promises.js

@ -8,6 +8,11 @@ let lastPromiseId = 1;
exports.setup = setupPromises; exports.setup = setupPromises;
function getAsynchronousRejectionWarningObject(uid) {
return new Error('Promise rejection was handled ' +
`asynchronously (rejection id: ${uid})`);
}
function setupPromises(scheduleMicrotasks) { function setupPromises(scheduleMicrotasks) {
process._setupPromises(function(event, promise, reason) { process._setupPromises(function(event, promise, reason) {
if (event === promiseRejectEvent.unhandled) if (event === promiseRejectEvent.unhandled)
@ -31,10 +36,15 @@ function setupPromises(scheduleMicrotasks) {
const uid = promiseToGuidProperty.get(promise); const uid = promiseToGuidProperty.get(promise);
promiseToGuidProperty.delete(promise); promiseToGuidProperty.delete(promise);
if (hasBeenNotified === true) { if (hasBeenNotified === true) {
let warning = null;
if (!process.listenerCount('rejectionHandled')) {
// Generate the warning object early to get a good stack trace.
warning = getAsynchronousRejectionWarningObject(uid);
}
process.nextTick(function() { process.nextTick(function() {
if (!process.emit('rejectionHandled', promise)) { if (!process.emit('rejectionHandled', promise)) {
const warning = new Error('Promise rejection was handled ' + if (warning === null)
`asynchronously (rejection id: ${uid})`); warning = getAsynchronousRejectionWarningObject(uid);
warning.name = 'PromiseRejectionHandledWarning'; warning.name = 'PromiseRejectionHandledWarning';
warning.id = uid; warning.id = uid;
process.emitWarning(warning); process.emitWarning(warning);
@ -50,6 +60,9 @@ function setupPromises(scheduleMicrotasks) {
`(rejection id: ${uid}): ${reason}`); `(rejection id: ${uid}): ${reason}`);
warning.name = 'UnhandledPromiseRejectionWarning'; warning.name = 'UnhandledPromiseRejectionWarning';
warning.id = uid; warning.id = uid;
if (reason instanceof Error) {
warning.stack = reason.stack;
}
process.emitWarning(warning); process.emitWarning(warning);
if (!deprecationWarned) { if (!deprecationWarned) {
deprecationWarned = true; deprecationWarned = true;

5
test/message/unhandled_promise_trace_warnings.js

@ -0,0 +1,5 @@
// Flags: --trace-warnings
'use strict';
require('../common');
const p = Promise.reject(new Error('This was rejected'));
setImmediate(() => p.catch(() => {}));

31
test/message/unhandled_promise_trace_warnings.out

@ -0,0 +1,31 @@
(node:*) Error: This was rejected
at * (*test*message*unhandled_promise_trace_warnings.js:*)
at *
at *
at *
at *
at *
at *
at *
at *
at *
(node:*) DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.
at *
at *
at *
at *
at *
at *
at *
at *
at *
(node:*) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
at getAsynchronousRejectionWarningObject (internal/process/promises.js:*)
at rejectionHandled (internal/process/promises.js:*)
at *
at Promise.then (native)
at Promise.catch (native)
at Immediate.setImmediate (*test*message*unhandled_promise_trace_warnings.js:*)
at *
at *
at *
Loading…
Cancel
Save