Permalink
Browse files

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>
  • Loading branch information...
benjamingr authored and jasnell committed Apr 28, 2016
1 parent 862610f commit 180867d6a6edd5e2bf6729696177a31c6908b161
Showing with 47 additions and 6 deletions.
  1. +21 −6 lib/internal/process/promises.js
  2. +26 −0 test/parallel/test-promises-warning-on-unhandled-rejection.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,32 +20,45 @@ function setupPromises(scheduleMicrotasks) {
function unhandledRejection(promise, reason) {
hasBeenNotifiedProperty.set(promise, false);
promiseToGuidProperty.set(promise, lastPromiseId++);
addPendingUnhandledRejection(promise, reason);
}
function rejectionHandled(promise) {
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);
}
});
}
}
}
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;
}
@@ -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(() => {})));

0 comments on commit 180867d

Please sign in to comment.