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/8223
Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com>
Reviewed-By: Chris Dickinson <christopher.s.dickinson@gmail.com>
v6.x
Benjamin Gruenbaum 9 years ago
committed by James M Snell
parent
commit
180867d6a6
  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 hasBeenNotifiedProperty = new WeakMap();
const promiseToGuidProperty = new WeakMap();
const pendingUnhandledRejections = [];
let lastPromiseId = 1;
exports.setup = setupPromises;
@ -18,6 +20,7 @@ function setupPromises(scheduleMicrotasks) {
function unhandledRejection(promise, reason) {
hasBeenNotifiedProperty.set(promise, false);
promiseToGuidProperty.set(promise, lastPromiseId++);
addPendingUnhandledRejection(promise, reason);
}
@ -25,9 +28,17 @@ function setupPromises(scheduleMicrotasks) {
var hasBeenNotified = hasBeenNotifiedProperty.get(promise);
if (hasBeenNotified !== undefined) {
hasBeenNotifiedProperty.delete(promise);
const uid = promiseToGuidProperty.get(promise);
promiseToGuidProperty.delete(promise);
if (hasBeenNotified === true) {
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() {
var hadListeners = false;
let hadListeners = false;
while (pendingUnhandledRejections.length > 0) {
var promise = pendingUnhandledRejections.shift();
var reason = pendingUnhandledRejections.shift();
const promise = pendingUnhandledRejections.shift();
const reason = pendingUnhandledRejections.shift();
if (hasBeenNotifiedProperty.get(promise) === false) {
hasBeenNotifiedProperty.set(promise, true);
const uid = promiseToGuidProperty.get(promise);
if (!process.emit('unhandledRejection', reason, promise)) {
// Nobody is listening.
// TODO(petkaantonov) Take some default action, see #830
const warning = new Error('Unhandled promise rejection ' +
`(rejection id: ${uid}): ${reason}`);
warning.name = 'UnhandledPromiseRejectionWarning';
warning.id = uid;
process.emitWarning(warning);
} else {
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