summaryrefslogtreecommitdiff
path: root/lib/internal/process/promises.js
diff options
context:
space:
mode:
Diffstat (limited to 'lib/internal/process/promises.js')
-rw-r--r--lib/internal/process/promises.js92
1 files changed, 76 insertions, 16 deletions
diff --git a/lib/internal/process/promises.js b/lib/internal/process/promises.js
index f3e118e2ce..95161ac7a8 100644
--- a/lib/internal/process/promises.js
+++ b/lib/internal/process/promises.js
@@ -1,6 +1,10 @@
'use strict';
-const { safeToString } = internalBinding('util');
+const { Object } = primordials;
+
+const {
+ safeToString
+} = internalBinding('util');
const {
tickInfo,
promiseRejectEvents: {
@@ -9,7 +13,8 @@ const {
kPromiseResolveAfterResolved,
kPromiseRejectAfterResolved
},
- setPromiseRejectCallback
+ setPromiseRejectCallback,
+ triggerFatalException
} = internalBinding('task_queue');
// *Must* match Environment::TickInfo::Fields in src/env.h.
@@ -20,6 +25,15 @@ const pendingUnhandledRejections = [];
const asyncHandledRejections = [];
let lastPromiseId = 0;
+const states = {
+ none: 0,
+ warn: 1,
+ strict: 2,
+ default: 3
+};
+
+let state;
+
function setHasRejectionToWarn(value) {
tickInfo[kHasRejectionToWarn] = value ? 1 : 0;
}
@@ -29,6 +43,10 @@ function hasRejectionToWarn() {
}
function promiseRejectHandler(type, promise, reason) {
+ if (state === undefined) {
+ const { getOptionValue } = require('internal/options');
+ state = states[getOptionValue('--unhandled-rejections') || 'default'];
+ }
switch (type) {
case kPromiseRejectWithNoHandler:
unhandledRejection(promise, reason);
@@ -59,6 +77,7 @@ function unhandledRejection(promise, reason) {
uid: ++lastPromiseId,
warned: false
});
+ // This causes the promise to be referenced at least for one tick.
pendingUnhandledRejections.push(promise);
setHasRejectionToWarn(true);
}
@@ -85,14 +104,16 @@ function handledRejection(promise) {
const unhandledRejectionErrName = 'UnhandledPromiseRejectionWarning';
function emitWarning(uid, reason) {
- // eslint-disable-next-line no-restricted-syntax
- const warning = new Error(
+ if (state === states.none) {
+ return;
+ }
+ const warning = getError(
+ unhandledRejectionErrName,
'Unhandled promise rejection. This error originated either by ' +
- 'throwing inside of an async function without a catch block, ' +
- 'or by rejecting a promise which was not handled with .catch(). ' +
- `(rejection id: ${uid})`
+ 'throwing inside of an async function without a catch block, ' +
+ 'or by rejecting a promise which was not handled with .catch(). ' +
+ `(rejection id: ${uid})`
);
- warning.name = unhandledRejectionErrName;
try {
if (reason instanceof Error) {
warning.stack = reason.stack;
@@ -108,7 +129,7 @@ function emitWarning(uid, reason) {
let deprecationWarned = false;
function emitDeprecationWarning() {
- if (!deprecationWarned) {
+ if (state === states.default && !deprecationWarned) {
deprecationWarned = true;
process.emitWarning(
'Unhandled promise rejections are deprecated. In the future, ' +
@@ -133,18 +154,57 @@ function processPromiseRejections() {
while (len--) {
const promise = pendingUnhandledRejections.shift();
const promiseInfo = maybeUnhandledPromises.get(promise);
- if (promiseInfo !== undefined) {
- promiseInfo.warned = true;
- const { reason, uid } = promiseInfo;
- if (!process.emit('unhandledRejection', reason, promise)) {
- emitWarning(uid, reason);
- }
- maybeScheduledTicks = true;
+ if (promiseInfo === undefined) {
+ continue;
+ }
+ 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);
+ }
+ maybeScheduledTicks = true;
}
return maybeScheduledTicks || pendingUnhandledRejections.length !== 0;
}
+function getError(name, message) {
+ // Reset the stack to prevent any overhead.
+ const tmp = Error.stackTraceLimit;
+ Error.stackTraceLimit = 0;
+ // eslint-disable-next-line no-restricted-syntax
+ const err = new Error(message);
+ Error.stackTraceLimit = tmp;
+ Object.defineProperty(err, 'name', {
+ value: name,
+ enumerable: false,
+ writable: true,
+ configurable: true,
+ });
+ return err;
+}
+
+function fatalException(reason) {
+ let err;
+ if (reason instanceof Error) {
+ err = reason;
+ } else {
+ err = getError(
+ 'UnhandledPromiseRejection',
+ 'This error originated either by ' +
+ 'throwing inside of an async function without a catch block, ' +
+ 'or by rejecting a promise which was not handled with .catch().' +
+ ` The promise rejected with the reason "${safeToString(reason)}".`
+ );
+ err.code = 'ERR_UNHANDLED_REJECTION';
+ }
+ triggerFatalException(err, true /* fromPromise */);
+}
+
function listenForRejections() {
setPromiseRejectCallback(promiseRejectHandler);
}