Skip to content

Commit 6f8b32e

Browse files
addaleaxMylesBorins
authored andcommitted
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: #9523 PR-URL: #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>
1 parent 687137e commit 6f8b32e

File tree

3 files changed

+41
-2
lines changed

3 files changed

+41
-2
lines changed

lib/internal/process/promises.js

+15-2
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ let lastPromiseId = 1;
88

99
exports.setup = setupPromises;
1010

11+
function getAsynchronousRejectionWarningObject(uid) {
12+
return new Error('Promise rejection was handled ' +
13+
`asynchronously (rejection id: ${uid})`);
14+
}
15+
1116
function setupPromises(scheduleMicrotasks) {
1217
process._setupPromises(function(event, promise, reason) {
1318
if (event === promiseRejectEvent.unhandled)
@@ -31,10 +36,15 @@ function setupPromises(scheduleMicrotasks) {
3136
const uid = promiseToGuidProperty.get(promise);
3237
promiseToGuidProperty.delete(promise);
3338
if (hasBeenNotified === true) {
39+
let warning = null;
40+
if (!process.listenerCount('rejectionHandled')) {
41+
// Generate the warning object early to get a good stack trace.
42+
warning = getAsynchronousRejectionWarningObject(uid);
43+
}
3444
process.nextTick(function() {
3545
if (!process.emit('rejectionHandled', promise)) {
36-
const warning = new Error('Promise rejection was handled ' +
37-
`asynchronously (rejection id: ${uid})`);
46+
if (warning === null)
47+
warning = getAsynchronousRejectionWarningObject(uid);
3848
warning.name = 'PromiseRejectionHandledWarning';
3949
warning.id = uid;
4050
process.emitWarning(warning);
@@ -58,6 +68,9 @@ function setupPromises(scheduleMicrotasks) {
5868
`(rejection id: ${uid}): ${reason}`);
5969
warning.name = 'UnhandledPromiseRejectionWarning';
6070
warning.id = uid;
71+
if (reason instanceof Error) {
72+
warning.stack = reason.stack;
73+
}
6174
process.emitWarning(warning);
6275
} else {
6376
hadListeners = true;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
// Flags: --trace-warnings
2+
'use strict';
3+
require('../common');
4+
const p = Promise.reject(new Error('This was rejected'));
5+
setImmediate(() => p.catch(() => {}));
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
(node:*) Error: This was rejected
2+
at * (*test*message*unhandled_promise_trace_warnings.js:*)
3+
at *
4+
at *
5+
at *
6+
at *
7+
at *
8+
at *
9+
at *
10+
at *
11+
at *
12+
(node:*) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)
13+
at getAsynchronousRejectionWarningObject (internal/process/promises.js:*)
14+
at rejectionHandled (internal/process/promises.js:*)
15+
at *
16+
at Promise.then (native)
17+
at Promise.catch (native)
18+
at Immediate.setImmediate (*test*message*unhandled_promise_trace_warnings.js:*)
19+
at *
20+
at *
21+
at *

0 commit comments

Comments
 (0)