Skip to content

Commit 180867d

Browse files
benjamingrjasnell
authored andcommitted
promise: warn on unhandled rejections
Log unhandled promise rejections with a guid and emit a process warning. When rejection is eventually handled, emit a secondary warning. PR-URL: #8223 Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Chris Dickinson <christopher.s.dickinson@gmail.com>
1 parent 862610f commit 180867d

File tree

2 files changed

+47
-6
lines changed

2 files changed

+47
-6
lines changed

lib/internal/process/promises.js

Lines changed: 21 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
const promiseRejectEvent = process._promiseRejectEvent;
44
const hasBeenNotifiedProperty = new WeakMap();
5+
const promiseToGuidProperty = new WeakMap();
56
const pendingUnhandledRejections = [];
7+
let lastPromiseId = 1;
68

79
exports.setup = setupPromises;
810

@@ -18,32 +20,45 @@ function setupPromises(scheduleMicrotasks) {
1820

1921
function unhandledRejection(promise, reason) {
2022
hasBeenNotifiedProperty.set(promise, false);
23+
promiseToGuidProperty.set(promise, lastPromiseId++);
2124
addPendingUnhandledRejection(promise, reason);
2225
}
2326

2427
function rejectionHandled(promise) {
2528
var hasBeenNotified = hasBeenNotifiedProperty.get(promise);
2629
if (hasBeenNotified !== undefined) {
2730
hasBeenNotifiedProperty.delete(promise);
31+
const uid = promiseToGuidProperty.get(promise);
32+
promiseToGuidProperty.delete(promise);
2833
if (hasBeenNotified === true) {
2934
process.nextTick(function() {
30-
process.emit('rejectionHandled', promise);
35+
if (!process.emit('rejectionHandled', promise)) {
36+
const warning = new Error('Promise rejection was handled ' +
37+
`asynchronously (rejection id: ${uid})`);
38+
warning.name = 'PromiseRejectionHandledWarning';
39+
warning.id = uid;
40+
process.emitWarning(warning);
41+
}
3142
});
3243
}
3344

3445
}
3546
}
3647

3748
function emitPendingUnhandledRejections() {
38-
var hadListeners = false;
49+
let hadListeners = false;
3950
while (pendingUnhandledRejections.length > 0) {
40-
var promise = pendingUnhandledRejections.shift();
41-
var reason = pendingUnhandledRejections.shift();
51+
const promise = pendingUnhandledRejections.shift();
52+
const reason = pendingUnhandledRejections.shift();
4253
if (hasBeenNotifiedProperty.get(promise) === false) {
4354
hasBeenNotifiedProperty.set(promise, true);
55+
const uid = promiseToGuidProperty.get(promise);
4456
if (!process.emit('unhandledRejection', reason, promise)) {
45-
// Nobody is listening.
46-
// TODO(petkaantonov) Take some default action, see #830
57+
const warning = new Error('Unhandled promise rejection ' +
58+
`(rejection id: ${uid}): ${reason}`);
59+
warning.name = 'UnhandledPromiseRejectionWarning';
60+
warning.id = uid;
61+
process.emitWarning(warning);
4762
} else {
4863
hadListeners = true;
4964
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// Flags: --no-warnings
2+
'use strict';
3+
4+
// Test that warnings are emitted when a Promise experiences an uncaught
5+
// rejection, and then again if the rejection is handled later on.
6+
7+
const common = require('../common');
8+
const assert = require('assert');
9+
10+
var b = 0;
11+
12+
process.on('warning', common.mustCall((warning) => {
13+
switch (b++) {
14+
case 0:
15+
assert.strictEqual(warning.name, 'UnhandledPromiseRejectionWarning');
16+
assert(/Unhandled promise rejection/.test(warning.message));
17+
break;
18+
case 1:
19+
assert.strictEqual(warning.name, 'PromiseRejectionHandledWarning');
20+
assert(/Promise rejection was handled asynchronously/
21+
.test(warning.message));
22+
}
23+
}, 2));
24+
25+
const p = Promise.reject('This was rejected');
26+
setImmediate(common.mustCall(() => p.catch(() => {})));

0 commit comments

Comments
 (0)