Browse Source

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: https://github.com/nodejs/node/pull/8217
Reviewed-By: Chris Dickinson <christopher.s.dickinson@gmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
v7.x
Benjamin Gruenbaum 9 years ago
committed by James M Snell
parent
commit
ecf474ceba
  1. 27
      lib/internal/process/promises.js
  2. 26
      test/parallel/test-promises-warning-on-unhandled-rejection.js

27
lib/internal/process/promises.js

@ -2,7 +2,9 @@
const promiseRejectEvent = process._promiseRejectEvent; const promiseRejectEvent = process._promiseRejectEvent;
const hasBeenNotifiedProperty = new WeakMap(); const hasBeenNotifiedProperty = new WeakMap();
const promiseToGuidProperty = new WeakMap();
const pendingUnhandledRejections = []; const pendingUnhandledRejections = [];
let lastPromiseId = 1;
exports.setup = setupPromises; exports.setup = setupPromises;
@ -18,6 +20,7 @@ function setupPromises(scheduleMicrotasks) {
function unhandledRejection(promise, reason) { function unhandledRejection(promise, reason) {
hasBeenNotifiedProperty.set(promise, false); hasBeenNotifiedProperty.set(promise, false);
promiseToGuidProperty.set(promise, lastPromiseId++);
addPendingUnhandledRejection(promise, reason); addPendingUnhandledRejection(promise, reason);
} }
@ -25,9 +28,17 @@ function setupPromises(scheduleMicrotasks) {
var hasBeenNotified = hasBeenNotifiedProperty.get(promise); var hasBeenNotified = hasBeenNotifiedProperty.get(promise);
if (hasBeenNotified !== undefined) { if (hasBeenNotified !== undefined) {
hasBeenNotifiedProperty.delete(promise); hasBeenNotifiedProperty.delete(promise);
const uid = promiseToGuidProperty.get(promise);
promiseToGuidProperty.delete(promise);
if (hasBeenNotified === true) { if (hasBeenNotified === true) {
process.nextTick(function() { process.nextTick(function() {
process.emit('rejectionHandled', promise); if (!process.emit('rejectionHandled', promise)) {
const warning = new Error('Promise rejection was handled ' +
`asynchronously (rejection id: ${uid})`);
warning.name = 'PromiseRejectionHandledWarning';
warning.id = uid;
process.emitWarning(warning);
}
}); });
} }
@ -35,15 +46,19 @@ function setupPromises(scheduleMicrotasks) {
} }
function emitPendingUnhandledRejections() { function emitPendingUnhandledRejections() {
var hadListeners = false; let hadListeners = false;
while (pendingUnhandledRejections.length > 0) { while (pendingUnhandledRejections.length > 0) {
var promise = pendingUnhandledRejections.shift(); const promise = pendingUnhandledRejections.shift();
var reason = pendingUnhandledRejections.shift(); const reason = pendingUnhandledRejections.shift();
if (hasBeenNotifiedProperty.get(promise) === false) { if (hasBeenNotifiedProperty.get(promise) === false) {
hasBeenNotifiedProperty.set(promise, true); hasBeenNotifiedProperty.set(promise, true);
const uid = promiseToGuidProperty.get(promise);
if (!process.emit('unhandledRejection', reason, promise)) { if (!process.emit('unhandledRejection', reason, promise)) {
// Nobody is listening. const warning = new Error('Unhandled promise rejection ' +
// TODO(petkaantonov) Take some default action, see #830 `(rejection id: ${uid}): ${reason}`);
warning.name = 'UnhandledPromiseRejectionWarning';
warning.id = uid;
process.emitWarning(warning);
} else { } else {
hadListeners = true; hadListeners = true;
} }

26
test/parallel/test-promises-warning-on-unhandled-rejection.js

@ -0,0 +1,26 @@
// Flags: --no-warnings
'use strict';
// Test that warnings are emitted when a Promise experiences an uncaught
// rejection, and then again if the rejection is handled later on.
const common = require('../common');
const assert = require('assert');
var b = 0;
process.on('warning', common.mustCall((warning) => {
switch (b++) {
case 0:
assert.strictEqual(warning.name, 'UnhandledPromiseRejectionWarning');
assert(/Unhandled promise rejection/.test(warning.message));
break;
case 1:
assert.strictEqual(warning.name, 'PromiseRejectionHandledWarning');
assert(/Promise rejection was handled asynchronously/
.test(warning.message));
}
}, 2));
const p = Promise.reject('This was rejected');
setImmediate(common.mustCall(() => p.catch(() => {})));
Loading…
Cancel
Save