Skip to content

Commit

Permalink
simplify processPromiseRejections
Browse files Browse the repository at this point in the history
  • Loading branch information
Uzlopak committed Mar 16, 2024
1 parent 84d9869 commit d221af4
Show file tree
Hide file tree
Showing 2 changed files with 122 additions and 103 deletions.
224 changes: 121 additions & 103 deletions lib/internal/process/promises.js
Original file line number Diff line number Diff line change
Expand Up @@ -164,59 +164,6 @@ const asyncHandledRejections = [];
*/
let lastPromiseId = 0;

// --unhandled-rejections=none:
// Emit 'unhandledRejection', but do not emit any warning.
const kIgnoreUnhandledRejections = 0;

// --unhandled-rejections=warn:
// Emit 'unhandledRejection', then emit 'UnhandledPromiseRejectionWarning'.
const kAlwaysWarnUnhandledRejections = 1;

// --unhandled-rejections=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 kStrictUnhandledRejections = 2;

// --unhandled-rejections=throw:
// Emit 'unhandledRejection', if it's unhandled, emit
// 'uncaughtException'. If it's not handled, print the error to stderr
// and exit the process.
const kThrowUnhandledRejections = 3;

// --unhandled-rejections=warn-with-error-code:
// Emit 'unhandledRejection', if it's unhandled, emit
// 'UnhandledPromiseRejectionWarning', then set process exit code to 1.
const kWarnWithErrorCodeUnhandledRejections = 4;

/**
* The mode of unhandled rejections.
* @type {0|1|2|3|4}
*/
let unhandledRejectionsMode;

/**
* @returns {0|1|2|3|4}
*/
function getUnhandledRejectionsMode() {
const { getOptionValue } = require('internal/options');
switch (getOptionValue('--unhandled-rejections')) {
case 'none':
return kIgnoreUnhandledRejections;
case 'warn':
return kAlwaysWarnUnhandledRejections;
case 'strict':
return kStrictUnhandledRejections;
case 'throw':
return kThrowUnhandledRejections;
case 'warn-with-error-code':
return kWarnWithErrorCodeUnhandledRejections;
default:
return kThrowUnhandledRejections;
}
}

/**
* @param {boolean} value
*/
Expand Down Expand Up @@ -390,6 +337,123 @@ function emitUnhandledRejectionWarning(uid, reason) {
process.emitWarning(warning);
}

/**
* @callback UnhandledRejectionsModeHandler
* @param {Error} reason
* @param {Promise} promise
* @param {PromiseInfo} promiseInfo
* @param {number} [promiseAsyncId]
* @returns {boolean}
*/

/**
* The mode of unhandled rejections.
* @type {UnhandledRejectionsModeHandler}
*/
let unhandledRejectionsMode;

/**
* --unhandled-rejections=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'.
* @type {UnhandledRejectionsModeHandler}
*/
function strictUnhandledRejectionsMode(reason, promise, promiseInfo, promiseAsyncId) {
const err = isErrorLike(reason) ?
reason : new UnhandledPromiseRejection(reason);
// This destroys the async stack, don't clear it after
triggerUncaughtException(err, true /* fromPromise */);
if (typeof promiseAsyncId !== 'undefined') {
pushAsyncContext(
promise[kAsyncIdSymbol],
promise[kTriggerAsyncIdSymbol],
promise,
);
}
const handled = emitUnhandledRejection(reason, promise, promiseInfo);
if (!handled) emitUnhandledRejectionWarning(promiseInfo.uid, reason);
return true;
}

/**
* --unhandled-rejections=none:
* Emit 'unhandledRejection', but do not emit any warning.
* @type {UnhandledRejectionsModeHandler}
*/
function ignoreUnhandledRejectionsMode(reason, promise, promiseInfo) {
emitUnhandledRejection(reason, promise, promiseInfo);
return true;
}

/**
* --unhandled-rejections=warn:
* Emit 'unhandledRejection', then emit 'UnhandledPromiseRejectionWarning'.
* @type {UnhandledRejectionsModeHandler}
*/
function alwaysWarnUnhandledRejectionsMode(reason, promise, promiseInfo) {
emitUnhandledRejection(reason, promise, promiseInfo);
emitUnhandledRejectionWarning(promiseInfo.uid, reason);
return true;
}

/**
* --unhandled-rejections=throw:
* Emit 'unhandledRejection', if it's unhandled, emit
* 'uncaughtException'. If it's not handled, print the error to stderr
* and exit the process.
* @type {UnhandledRejectionsModeHandler}
*/
function throwUnhandledRejectionsMode(reason, promise, promiseInfo) {
const handled = emitUnhandledRejection(reason, promise, promiseInfo);
if (!handled) {
const err = isErrorLike(reason) ?
reason :
new UnhandledPromiseRejection(reason);
// This destroys the async stack, don't clear it after
triggerUncaughtException(err, true /* fromPromise */);
return false;
}
return true;
}

/**
* --unhandled-rejections=warn-with-error-code:
* Emit 'unhandledRejection', if it's unhandled, emit
* 'UnhandledPromiseRejectionWarning', then set process exit code to 1.
* @type {UnhandledRejectionsModeHandler}
*/
function warnWithErrorCodeUnhandledRejectionsMode(reason, promise, promiseInfo) {
const handled = emitUnhandledRejection(reason, promise, promiseInfo);
if (!handled) {
emitUnhandledRejectionWarning(promiseInfo.uid, reason);
process.exitCode = kGenericUserError;
}
return true;
}

/**
* @returns {UnhandledRejectionsModeHandler}
*/
function getUnhandledRejectionsMode() {
const { getOptionValue } = require('internal/options');
switch (getOptionValue('--unhandled-rejections')) {
case 'none':
return ignoreUnhandledRejectionsMode;
case 'warn':
return alwaysWarnUnhandledRejectionsMode;
case 'strict':
return strictUnhandledRejectionsMode;
case 'throw':
return throwUnhandledRejectionsMode;
case 'warn-with-error-code':
return warnWithErrorCodeUnhandledRejectionsMode;
default:
return throwUnhandledRejectionsMode;
}
}

// If this method returns true, we've executed user code or triggered
// a warning to be emitted which requires the microtask and next tick
// queues to be drained again.
Expand All @@ -404,16 +468,17 @@ function processPromiseRejections() {
}

let len = pendingUnhandledRejections.length;
let needPop = true;

while (len--) {
const promise = ArrayPrototypeShift(pendingUnhandledRejections);
const promiseInfo = maybeUnhandledPromises.get(promise);
if (promiseInfo === undefined) {
continue;
}
promiseInfo.warned = true;
const { reason, uid } = promiseInfo;
const { reason } = promiseInfo;

let needPop = true;
const {
[kAsyncIdSymbol]: promiseAsyncId,
[kTriggerAsyncIdSymbol]: promiseTriggerAsyncId,
Expand All @@ -429,54 +494,7 @@ function processPromiseRejections() {
);
}
try {
switch (unhandledRejectionsMode) {
case kStrictUnhandledRejections: {
const err = isErrorLike(reason) ?
reason : new UnhandledPromiseRejection(reason);
// This destroys the async stack, don't clear it after
triggerUncaughtException(err, true /* fromPromise */);
if (typeof promiseAsyncId !== 'undefined') {
pushAsyncContext(
promise[kAsyncIdSymbol],
promise[kTriggerAsyncIdSymbol],
promise,
);
}
const handled = emitUnhandledRejection(reason, promise, promiseInfo);
if (!handled) emitUnhandledRejectionWarning(uid, reason);
break;
}
case kIgnoreUnhandledRejections: {
emitUnhandledRejection(reason, promise, promiseInfo);
break;
}
case kAlwaysWarnUnhandledRejections: {
emitUnhandledRejection(reason, promise, promiseInfo);
emitUnhandledRejectionWarning(uid, reason);
break;
}
case kThrowUnhandledRejections: {
const handled = emitUnhandledRejection(reason, promise, promiseInfo);
if (!handled) {
const err = isErrorLike(reason) ?
reason :
new UnhandledPromiseRejection(reason);

// This destroys the async stack, don't clear it after
triggerUncaughtException(err, true /* fromPromise */);
needPop = false;
}
break;
}
case kWarnWithErrorCodeUnhandledRejections: {
const handled = emitUnhandledRejection(reason, promise, promiseInfo);
if (!handled) {
emitUnhandledRejectionWarning(uid, reason);
process.exitCode = kGenericUserError;
}
break;
}
}
needPop = unhandledRejectionsMode(reason, promise, promiseInfo, promiseAsyncId);
} finally {
if (needPop && typeof promiseAsyncId !== 'undefined') {
popAsyncContext(promiseAsyncId);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
at *
at *
at *
at *
(node:*) Error: This was rejected
at *
at *
Expand Down

0 comments on commit d221af4

Please sign in to comment.