-
Notifications
You must be signed in to change notification settings - Fork 29.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
process: refactor unhandled rejection handling #28228
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,14 +25,24 @@ const pendingUnhandledRejections = []; | |
const asyncHandledRejections = []; | ||
let lastPromiseId = 0; | ||
|
||
const states = { | ||
none: 0, | ||
warn: 1, | ||
strict: 2, | ||
default: 3 | ||
}; | ||
|
||
let state; | ||
// --unhandled-rejection=none: | ||
// Emit 'unhandledRejection', but do not emit any warning. | ||
const kIgnoreUnhandledRejections = 0; | ||
// --unhandled-rejection=warn: | ||
// Emit 'unhandledRejection', then emit 'UnhandledPromiseRejectionWarning'. | ||
const kAlwaysWarnUnhandledRejections = 1; | ||
// --unhandled-rejection=strict: | ||
// Emit 'uncaughtException'. If it's not handled, print the error to stderr | ||
// and exit the process. | ||
// Otherwise, emit 'unhandledRejection'. If 'unhandledRejection' is not | ||
// handled, emit 'UnhandledPromiseRejectionWarning'. | ||
const kThrowUnhandledRejections = 2; | ||
// --unhandled-rejection is unset: | ||
// Emit 'unhandledRejection', if it's handled, emit | ||
// 'UnhandledPromiseRejectionWarning', then emit deprecation warning. | ||
const kDefaultUnhandledRejections = 3; | ||
|
||
let unhandledRejectionsMode; | ||
|
||
function setHasRejectionToWarn(value) { | ||
tickInfo[kHasRejectionToWarn] = value ? 1 : 0; | ||
|
@@ -42,10 +52,23 @@ function hasRejectionToWarn() { | |
return tickInfo[kHasRejectionToWarn] === 1; | ||
} | ||
|
||
function getUnhandledRejectionsMode() { | ||
const { getOptionValue } = require('internal/options'); | ||
switch (getOptionValue('--unhandled-rejections')) { | ||
case 'none': | ||
return kIgnoreUnhandledRejections; | ||
case 'warn': | ||
return kAlwaysWarnUnhandledRejections; | ||
case 'strict': | ||
return kThrowUnhandledRejections; | ||
default: | ||
return kDefaultUnhandledRejections; | ||
} | ||
} | ||
|
||
function promiseRejectHandler(type, promise, reason) { | ||
if (state === undefined) { | ||
const { getOptionValue } = require('internal/options'); | ||
state = states[getOptionValue('--unhandled-rejections') || 'default']; | ||
if (unhandledRejectionsMode === undefined) { | ||
unhandledRejectionsMode = getUnhandledRejectionsMode(); | ||
} | ||
switch (type) { | ||
case kPromiseRejectWithNoHandler: | ||
|
@@ -104,9 +127,6 @@ function handledRejection(promise) { | |
|
||
const unhandledRejectionErrName = 'UnhandledPromiseRejectionWarning'; | ||
function emitWarning(uid, reason) { | ||
if (state === states.none) { | ||
return; | ||
} | ||
const warning = getError( | ||
unhandledRejectionErrName, | ||
'Unhandled promise rejection. This error originated either by ' + | ||
|
@@ -129,7 +149,8 @@ function emitWarning(uid, reason) { | |
|
||
let deprecationWarned = false; | ||
function emitDeprecationWarning() { | ||
if (state === states.default && !deprecationWarned) { | ||
if (unhandledRejectionsMode === kDefaultUnhandledRejections && | ||
!deprecationWarned) { | ||
deprecationWarned = true; | ||
process.emitWarning( | ||
'Unhandled promise rejections are deprecated. In the future, ' + | ||
|
@@ -161,13 +182,27 @@ function processPromiseRejections() { | |
} | ||
promiseInfo.warned = true; | ||
const { reason, uid } = promiseInfo; | ||
if (state === states.strict) { | ||
fatalException(reason); | ||
} | ||
if (!process.emit('unhandledRejection', reason, promise) || | ||
// Always warn in case the user requested it. | ||
state === states.warn) { | ||
emitWarning(uid, reason); | ||
switch (unhandledRejectionsMode) { | ||
case kThrowUnhandledRejections: { | ||
fatalException(reason); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also funny enough there really isn't anything fatal about this function, or There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a legacy name. It does however reflect it's actual purpose. It is just unfortunate that we have an escape hatch for it with |
||
const handled = process.emit('unhandledRejection', reason, promise); | ||
if (!handled) emitWarning(uid, reason); | ||
break; | ||
} | ||
case kIgnoreUnhandledRejections: { | ||
process.emit('unhandledRejection', reason, promise); | ||
break; | ||
} | ||
case kAlwaysWarnUnhandledRejections: { | ||
process.emit('unhandledRejection', reason, promise); | ||
emitWarning(uid, reason); | ||
break; | ||
} | ||
case kDefaultUnhandledRejections: { | ||
const handled = process.emit('unhandledRejection', reason, promise); | ||
if (!handled) emitWarning(uid, reason); | ||
break; | ||
} | ||
} | ||
maybeScheduledTicksOrMicrotasks = true; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TBH this behavior (currently on master) looks weird to me. It contradicts with the description in
test/parallel/test-promise-unhandled-error.js
:Even though that test specifically tests:
node/test/parallel/test-promise-unhandled-error.js
Line 37 in 282e2f6
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cc @BridgeAR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The test description is faulty. The original implementation changed multiple times and it seems like the description was not updated appropriately.
The original intention was to replace the unhandled rejections with the exception. Later on it was decided that the current unhandled rejections hook behavior should not be influenced by the flag at all.