mirror of
https://github.com/denoland/deno.git
synced 2025-02-01 20:25:12 -05:00
feat: "rejectionhandled" Web event and "rejectionHandled" Node event (#21875)
This commit adds support for "rejectionhandled" Web Event and "rejectionHandled" Node event. ```js import process from "node:process"; process.on("rejectionHandled", (promise) => { console.log("rejectionHandled", reason, promise); }); window.addEventListener("rejectionhandled", (event) => { console.log("rejectionhandled", event.reason, event.promise); }); ``` --------- Co-authored-by: Matt Mastracci <matthew@mastracci.com>
This commit is contained in:
parent
288774c5ed
commit
7471587d29
9 changed files with 106 additions and 1 deletions
|
@ -193,3 +193,12 @@ itest!(unhandled_rejection_web_process {
|
||||||
envs: env_vars_for_npm_tests(),
|
envs: env_vars_for_npm_tests(),
|
||||||
http_server: true,
|
http_server: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Ensure that Web `onrejectionhandled` is fired before
|
||||||
|
// Node's `process.on('rejectionHandled')`.
|
||||||
|
itest!(rejection_handled_web_process {
|
||||||
|
args: "run -A node/rejection_handled_web_process.ts",
|
||||||
|
output: "node/rejection_handled_web_process.ts.out",
|
||||||
|
envs: env_vars_for_npm_tests(),
|
||||||
|
http_server: true,
|
||||||
|
});
|
||||||
|
|
|
@ -3672,6 +3672,11 @@ itest!(unhandled_rejection_dynamic_import2 {
|
||||||
output: "run/unhandled_rejection_dynamic_import2/main.ts.out",
|
output: "run/unhandled_rejection_dynamic_import2/main.ts.out",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
itest!(rejection_handled {
|
||||||
|
args: "run --check run/rejection_handled.ts",
|
||||||
|
output: "run/rejection_handled.out",
|
||||||
|
});
|
||||||
|
|
||||||
itest!(nested_error {
|
itest!(nested_error {
|
||||||
args: "run run/nested_error/main.ts",
|
args: "run run/nested_error/main.ts",
|
||||||
output: "run/nested_error/main.ts.out",
|
output: "run/nested_error/main.ts.out",
|
||||||
|
|
26
cli/tests/testdata/node/rejection_handled_web_process.ts
vendored
Normal file
26
cli/tests/testdata/node/rejection_handled_web_process.ts
vendored
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
import chalk from "npm:chalk";
|
||||||
|
import process from "node:process";
|
||||||
|
|
||||||
|
console.log(chalk.red("Hello world!"));
|
||||||
|
|
||||||
|
globalThis.addEventListener("unhandledrejection", (e) => {
|
||||||
|
console.log('globalThis.addEventListener("unhandledrejection");');
|
||||||
|
e.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
globalThis.addEventListener("rejectionhandled", (_) => {
|
||||||
|
console.log("Web rejectionhandled");
|
||||||
|
});
|
||||||
|
|
||||||
|
process.on("rejectionHandled", (_) => {
|
||||||
|
console.log("Node rejectionHandled");
|
||||||
|
});
|
||||||
|
|
||||||
|
const a = Promise.reject(1);
|
||||||
|
setTimeout(() => {
|
||||||
|
a.catch(() => console.log("Added catch handler to the promise"));
|
||||||
|
}, 10);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
console.log("Success");
|
||||||
|
}, 50);
|
7
cli/tests/testdata/node/rejection_handled_web_process.ts.out
vendored
Normal file
7
cli/tests/testdata/node/rejection_handled_web_process.ts.out
vendored
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
[WILDCARD]
|
||||||
|
Hello world!
|
||||||
|
globalThis.addEventListener("unhandledrejection");
|
||||||
|
Added catch handler to the promise
|
||||||
|
Web rejectionhandled
|
||||||
|
Node rejectionHandled
|
||||||
|
Success
|
5
cli/tests/testdata/run/rejection_handled.out
vendored
Normal file
5
cli/tests/testdata/run/rejection_handled.out
vendored
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
[WILDCARD]
|
||||||
|
unhandledrejection 1 Promise { <rejected> 1 }
|
||||||
|
Added catch handler to the promise
|
||||||
|
rejectionhandled 1 Promise { <rejected> 1 }
|
||||||
|
Success
|
17
cli/tests/testdata/run/rejection_handled.ts
vendored
Normal file
17
cli/tests/testdata/run/rejection_handled.ts
vendored
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
window.addEventListener("unhandledrejection", (event) => {
|
||||||
|
console.log("unhandledrejection", event.reason, event.promise);
|
||||||
|
event.preventDefault();
|
||||||
|
});
|
||||||
|
|
||||||
|
window.addEventListener("rejectionhandled", (event) => {
|
||||||
|
console.log("rejectionhandled", event.reason, event.promise);
|
||||||
|
});
|
||||||
|
|
||||||
|
const a = Promise.reject(1);
|
||||||
|
setTimeout(async () => {
|
||||||
|
a.catch(() => console.log("Added catch handler to the promise"));
|
||||||
|
}, 10);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
console.log("Success");
|
||||||
|
}, 50);
|
4
cli/tsc/dts/lib.deno.window.d.ts
vendored
4
cli/tsc/dts/lib.deno.window.d.ts
vendored
|
@ -12,6 +12,7 @@
|
||||||
declare interface WindowEventMap {
|
declare interface WindowEventMap {
|
||||||
"error": ErrorEvent;
|
"error": ErrorEvent;
|
||||||
"unhandledrejection": PromiseRejectionEvent;
|
"unhandledrejection": PromiseRejectionEvent;
|
||||||
|
"rejectionhandled": PromiseRejectionEvent;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @category Web APIs */
|
/** @category Web APIs */
|
||||||
|
@ -25,6 +26,9 @@ declare interface Window extends EventTarget {
|
||||||
onunhandledrejection:
|
onunhandledrejection:
|
||||||
| ((this: Window, ev: PromiseRejectionEvent) => any)
|
| ((this: Window, ev: PromiseRejectionEvent) => any)
|
||||||
| null;
|
| null;
|
||||||
|
onrejectionhandled:
|
||||||
|
| ((this: Window, ev: PromiseRejectionEvent) => any)
|
||||||
|
| null;
|
||||||
close: () => void;
|
close: () => void;
|
||||||
readonly closed: boolean;
|
readonly closed: boolean;
|
||||||
alert: (message?: string) => void;
|
alert: (message?: string) => void;
|
||||||
|
|
|
@ -75,7 +75,6 @@ import { buildAllowedFlags } from "ext:deno_node/internal/process/per_thread.mjs
|
||||||
|
|
||||||
const notImplementedEvents = [
|
const notImplementedEvents = [
|
||||||
"multipleResolves",
|
"multipleResolves",
|
||||||
"rejectionHandled",
|
|
||||||
"worker",
|
"worker",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -746,6 +745,7 @@ export const removeListener = process.removeListener;
|
||||||
export const removeAllListeners = process.removeAllListeners;
|
export const removeAllListeners = process.removeAllListeners;
|
||||||
|
|
||||||
let unhandledRejectionListenerCount = 0;
|
let unhandledRejectionListenerCount = 0;
|
||||||
|
let rejectionHandledListenerCount = 0;
|
||||||
let uncaughtExceptionListenerCount = 0;
|
let uncaughtExceptionListenerCount = 0;
|
||||||
let beforeExitListenerCount = 0;
|
let beforeExitListenerCount = 0;
|
||||||
let exitListenerCount = 0;
|
let exitListenerCount = 0;
|
||||||
|
@ -755,6 +755,9 @@ process.on("newListener", (event: string) => {
|
||||||
case "unhandledRejection":
|
case "unhandledRejection":
|
||||||
unhandledRejectionListenerCount++;
|
unhandledRejectionListenerCount++;
|
||||||
break;
|
break;
|
||||||
|
case "rejectionHandled":
|
||||||
|
rejectionHandledListenerCount++;
|
||||||
|
break;
|
||||||
case "uncaughtException":
|
case "uncaughtException":
|
||||||
uncaughtExceptionListenerCount++;
|
uncaughtExceptionListenerCount++;
|
||||||
break;
|
break;
|
||||||
|
@ -775,6 +778,9 @@ process.on("removeListener", (event: string) => {
|
||||||
case "unhandledRejection":
|
case "unhandledRejection":
|
||||||
unhandledRejectionListenerCount--;
|
unhandledRejectionListenerCount--;
|
||||||
break;
|
break;
|
||||||
|
case "rejectionHandled":
|
||||||
|
rejectionHandledListenerCount--;
|
||||||
|
break;
|
||||||
case "uncaughtException":
|
case "uncaughtException":
|
||||||
uncaughtExceptionListenerCount--;
|
uncaughtExceptionListenerCount--;
|
||||||
break;
|
break;
|
||||||
|
@ -837,6 +843,16 @@ function synchronizeListeners() {
|
||||||
internals.nodeProcessUnhandledRejectionCallback = undefined;
|
internals.nodeProcessUnhandledRejectionCallback = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Install special "handledrejection" handler, that will be called
|
||||||
|
// last.
|
||||||
|
if (rejectionHandledListenerCount > 0) {
|
||||||
|
internals.nodeProcessRejectionHandledCallback = (event) => {
|
||||||
|
process.emit("rejectionHandled", event.reason, event.promise);
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
internals.nodeProcessRejectionHandledCallback = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
if (uncaughtExceptionListenerCount > 0) {
|
if (uncaughtExceptionListenerCount > 0) {
|
||||||
globalThis.addEventListener("error", processOnError);
|
globalThis.addEventListener("error", processOnError);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -344,6 +344,8 @@ function runtimeStart(
|
||||||
}
|
}
|
||||||
|
|
||||||
core.setUnhandledPromiseRejectionHandler(processUnhandledPromiseRejection);
|
core.setUnhandledPromiseRejectionHandler(processUnhandledPromiseRejection);
|
||||||
|
core.setHandledPromiseRejectionHandler(processRejectionHandled);
|
||||||
|
|
||||||
// Notification that the core received an unhandled promise rejection that is about to
|
// Notification that the core received an unhandled promise rejection that is about to
|
||||||
// terminate the runtime. If we can handle it, attempt to do so.
|
// terminate the runtime. If we can handle it, attempt to do so.
|
||||||
function processUnhandledPromiseRejection(promise, reason) {
|
function processUnhandledPromiseRejection(promise, reason) {
|
||||||
|
@ -377,6 +379,20 @@ function processUnhandledPromiseRejection(promise, reason) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function processRejectionHandled(promise, reason) {
|
||||||
|
const rejectionHandledEvent = new event.PromiseRejectionEvent(
|
||||||
|
"rejectionhandled",
|
||||||
|
{ promise, reason },
|
||||||
|
);
|
||||||
|
|
||||||
|
// Note that the handler may throw, causing a recursive "error" event
|
||||||
|
globalThis_.dispatchEvent(rejectionHandledEvent);
|
||||||
|
|
||||||
|
if (typeof internals.nodeProcessRejectionHandledCallback !== "undefined") {
|
||||||
|
internals.nodeProcessRejectionHandledCallback(rejectionHandledEvent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let hasBootstrapped = false;
|
let hasBootstrapped = false;
|
||||||
// Delete the `console` object that V8 automaticaly adds onto the global wrapper
|
// Delete the `console` object that V8 automaticaly adds onto the global wrapper
|
||||||
// object on context creation. We don't want this console object to shadow the
|
// object on context creation. We don't want this console object to shadow the
|
||||||
|
|
Loading…
Add table
Reference in a new issue