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(),
|
||||
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",
|
||||
});
|
||||
|
||||
itest!(rejection_handled {
|
||||
args: "run --check run/rejection_handled.ts",
|
||||
output: "run/rejection_handled.out",
|
||||
});
|
||||
|
||||
itest!(nested_error {
|
||||
args: "run run/nested_error/main.ts",
|
||||
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 {
|
||||
"error": ErrorEvent;
|
||||
"unhandledrejection": PromiseRejectionEvent;
|
||||
"rejectionhandled": PromiseRejectionEvent;
|
||||
}
|
||||
|
||||
/** @category Web APIs */
|
||||
|
@ -25,6 +26,9 @@ declare interface Window extends EventTarget {
|
|||
onunhandledrejection:
|
||||
| ((this: Window, ev: PromiseRejectionEvent) => any)
|
||||
| null;
|
||||
onrejectionhandled:
|
||||
| ((this: Window, ev: PromiseRejectionEvent) => any)
|
||||
| null;
|
||||
close: () => void;
|
||||
readonly closed: boolean;
|
||||
alert: (message?: string) => void;
|
||||
|
|
|
@ -75,7 +75,6 @@ import { buildAllowedFlags } from "ext:deno_node/internal/process/per_thread.mjs
|
|||
|
||||
const notImplementedEvents = [
|
||||
"multipleResolves",
|
||||
"rejectionHandled",
|
||||
"worker",
|
||||
];
|
||||
|
||||
|
@ -746,6 +745,7 @@ export const removeListener = process.removeListener;
|
|||
export const removeAllListeners = process.removeAllListeners;
|
||||
|
||||
let unhandledRejectionListenerCount = 0;
|
||||
let rejectionHandledListenerCount = 0;
|
||||
let uncaughtExceptionListenerCount = 0;
|
||||
let beforeExitListenerCount = 0;
|
||||
let exitListenerCount = 0;
|
||||
|
@ -755,6 +755,9 @@ process.on("newListener", (event: string) => {
|
|||
case "unhandledRejection":
|
||||
unhandledRejectionListenerCount++;
|
||||
break;
|
||||
case "rejectionHandled":
|
||||
rejectionHandledListenerCount++;
|
||||
break;
|
||||
case "uncaughtException":
|
||||
uncaughtExceptionListenerCount++;
|
||||
break;
|
||||
|
@ -775,6 +778,9 @@ process.on("removeListener", (event: string) => {
|
|||
case "unhandledRejection":
|
||||
unhandledRejectionListenerCount--;
|
||||
break;
|
||||
case "rejectionHandled":
|
||||
rejectionHandledListenerCount--;
|
||||
break;
|
||||
case "uncaughtException":
|
||||
uncaughtExceptionListenerCount--;
|
||||
break;
|
||||
|
@ -837,6 +843,16 @@ function synchronizeListeners() {
|
|||
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) {
|
||||
globalThis.addEventListener("error", processOnError);
|
||||
} else {
|
||||
|
|
|
@ -344,6 +344,8 @@ function runtimeStart(
|
|||
}
|
||||
|
||||
core.setUnhandledPromiseRejectionHandler(processUnhandledPromiseRejection);
|
||||
core.setHandledPromiseRejectionHandler(processRejectionHandled);
|
||||
|
||||
// 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.
|
||||
function processUnhandledPromiseRejection(promise, reason) {
|
||||
|
@ -377,6 +379,20 @@ function processUnhandledPromiseRejection(promise, reason) {
|
|||
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;
|
||||
// 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
|
||||
|
|
Loading…
Add table
Reference in a new issue