1
0
Fork 0
mirror of https://github.com/denoland/deno.git synced 2024-11-26 16:09:27 -05:00

fix(core): run macrotasks and next ticks after polling dynamic imports (#17173)

This commit fixes handling of rejected promises in dynamic imports
evaluation.

Previously we were running callbacks for next ticks and macrotasks
_before_ polling
dynamic imports and checked for unhandled rejections immediately after.
This is wrong,
as `unhandledrejection` event is dispatched and its callbacks are run as
macrotasks.

This commit changes order of actions performed by the event loop to
following:
- poll async ops
- poll dynamic imports
- run next tick callbacks
- run macrotask callbacks
- check for unhandled promise rejections
This commit is contained in:
Bartek Iwańczuk 2022-12-23 19:46:24 +01:00
parent 36a936c5aa
commit 127b68df39
No known key found for this signature in database
GPG key ID: 0C6BCDDC3B3AD750
8 changed files with 58 additions and 8 deletions

View file

@ -2945,6 +2945,19 @@ mod run {
output: "run/unhandled_rejection_sync_error.ts.out", output: "run/unhandled_rejection_sync_error.ts.out",
}); });
// Regression test for https://github.com/denoland/deno/issues/15661
itest!(unhandled_rejection_dynamic_import {
args: "run --allow-read run/unhandled_rejection_dynamic_import/main.ts",
output: "run/unhandled_rejection_dynamic_import/main.ts.out",
exit_code: 1,
});
// Regression test for https://github.com/denoland/deno/issues/16909
itest!(unhandled_rejection_dynamic_import2 {
args: "run --allow-read run/unhandled_rejection_dynamic_import2/main.ts",
output: "run/unhandled_rejection_dynamic_import2/main.ts.out",
});
itest!(nested_error { itest!(nested_error {
args: "run run/nested_error.ts", args: "run run/nested_error.ts",
output: "run/nested_error.ts.out", output: "run/nested_error.ts.out",

View file

@ -0,0 +1,5 @@
globalThis.addEventListener("unhandledrejection", () => {
console.log("hey");
});
console.log("---");
Promise.reject();

View file

@ -0,0 +1 @@
await import("./import.ts");

View file

@ -0,0 +1,3 @@
---
hey
error: Uncaught (in promise) undefined

View file

@ -0,0 +1,3 @@
export default {
a: "a",
};

View file

@ -0,0 +1,21 @@
globalThis.addEventListener("unhandledrejection", (e) => {
console.log("unhandled rejection", e.reason);
e.preventDefault();
});
const dummyImport = (await import("./import.ts")).default;
let a = new Promise((resolve, reject) => {
throw "errA";
});
let i = 0;
while (true) {
await new Promise((resolve) => setTimeout(resolve, 100));
i++;
console.log("running...");
if (i > 3) {
break;
}
}

View file

@ -0,0 +1,5 @@
unhandled rejection errA
running...
running...
running...
running...

View file

@ -1127,12 +1127,7 @@ impl JsRuntime {
self.pump_v8_message_loop()?; self.pump_v8_message_loop()?;
// Ops // Ops
{
self.resolve_async_ops(cx)?; self.resolve_async_ops(cx)?;
self.drain_nexttick()?;
self.drain_macrotasks()?;
self.check_promise_exceptions()?;
}
// Dynamic module loading - ie. modules loaded using "import()" // Dynamic module loading - ie. modules loaded using "import()"
{ {
// Run in a loop so that dynamic imports that only depend on another // Run in a loop so that dynamic imports that only depend on another
@ -1157,9 +1152,13 @@ impl JsRuntime {
break; break;
} }
} }
self.check_promise_exceptions()?;
} }
// Run all next tick callbacks and macrotasks callbacks and only then
// check for any promise exceptions (`unhandledrejection` handlers are
// run in macrotasks callbacks so we need to let them run first).
self.drain_nexttick()?;
self.drain_macrotasks()?;
self.check_promise_exceptions()?;
// Event loop middlewares // Event loop middlewares
let mut maybe_scheduling = false; let mut maybe_scheduling = false;