diff options
author | Bartek IwaĆczuk <biwanczuk@gmail.com> | 2022-07-04 21:14:58 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-07-04 21:14:58 +0200 |
commit | f7af0b01a59aaac91473e2f920137004d39a310a (patch) | |
tree | f04bdb8c0acba2730cff47a91b249e15543435a8 /runtime/js | |
parent | 691d67b3ed788e30b17600d1dc472ecfa83b6585 (diff) |
feat: add "unhandledrejection" event support (#12994)
This commit adds support for "unhandledrejection" event.
This event will trigger event listeners registered using:
"globalThis.addEventListener("unhandledrejection")
"globalThis.onunhandledrejection"
This is done by registering a default handler using
"Deno.core.setPromiseRejectCallback" that allows to
handle rejected promises in JavaScript instead of Rust.
This commit will make it possible to polyfill
"process.on("unhandledRejection")" in the Node compat
layer.
Co-authored-by: Colin Ihrig <cjihrig@gmail.com>
Diffstat (limited to 'runtime/js')
-rw-r--r-- | runtime/js/99_main.js | 46 |
1 files changed, 46 insertions, 0 deletions
diff --git a/runtime/js/99_main.js b/runtime/js/99_main.js index c13faa936..ecda556eb 100644 --- a/runtime/js/99_main.js +++ b/runtime/js/99_main.js @@ -411,6 +411,7 @@ delete Intl.v8BreakIterator; PerformanceEntry: util.nonEnumerable(performance.PerformanceEntry), PerformanceMark: util.nonEnumerable(performance.PerformanceMark), PerformanceMeasure: util.nonEnumerable(performance.PerformanceMeasure), + PromiseRejectionEvent: util.nonEnumerable(PromiseRejectionEvent), ProgressEvent: util.nonEnumerable(ProgressEvent), ReadableStream: util.nonEnumerable(streams.ReadableStream), ReadableStreamDefaultReader: util.nonEnumerable( @@ -553,6 +554,43 @@ delete Intl.v8BreakIterator; postMessage: util.writable(postMessage), }; + function promiseRejectCallback(type, promise, reason) { + switch (type) { + case 0: { + core.opSync("op_store_pending_promise_exception", promise, reason); + break; + } + case 1: { + core.opSync("op_remove_pending_promise_exception", promise); + break; + } + default: + return; + } + core.opAsync("op_next_task").then(() => { + const hasPendingException = core.opSync( + "op_has_pending_promise_exception", + promise, + ); + + if (!hasPendingException) { + return; + } + + const event = new PromiseRejectionEvent("unhandledrejection", { + cancelable: true, + promise, + reason, + }); + globalThis.dispatchEvent(event); + + // If event was not prevented we will let Rust side handle it. + if (event.defaultPrevented) { + core.opSync("op_remove_pending_promise_exception", promise); + } + }); + } + let hasBootstrapped = false; function bootstrapMainRuntime(runtimeOptions) { @@ -585,6 +623,10 @@ delete Intl.v8BreakIterator; defineEventHandler(window, "load"); defineEventHandler(window, "beforeunload"); defineEventHandler(window, "unload"); + defineEventHandler(window, "unhandledrejection"); + + core.setPromiseRejectCallback(promiseRejectCallback); + const isUnloadDispatched = SymbolFor("isUnloadDispatched"); // Stores the flag for checking whether unload is dispatched or not. // This prevents the recursive dispatches of unload events. @@ -685,6 +727,10 @@ delete Intl.v8BreakIterator; defineEventHandler(self, "message"); defineEventHandler(self, "error", undefined, true); + defineEventHandler(self, "unhandledrejection"); + + core.setPromiseRejectCallback(promiseRejectCallback); + // `Deno.exit()` is an alias to `self.close()`. Setting and exit // code using an op in worker context is a no-op. os.setExitHandler((_exitCode) => { |