Fetch ordered dynamic scripts concurrently

This commit is contained in:
SGavrl committed 2026-08-04 09:53:24 +00:00
1 parent 3db63d9125
commit 99ff0601bf
2 files changed
+49 -24

No files matched your search

@@ -109,7 +109,7 @@ async fn serve_dynamic_order_fixture(explicitly_in_order: bool) -> (String, Arc<
"window.__dynamicOrder.push('slow');".to_string(),
)
} else if request.starts_with("GET /fast.js") {
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
tokio::time::sleep(std::time::Duration::from_millis(500)).await;
(
"application/javascript",
"window.__dynamicOrder.push('fast');".to_string(),
@@ -199,12 +199,12 @@ async fn dynamic_classic_fetch_concurrency_matches_force_async_state() {
navigate_dynamic_order_fixture(true).await;
assert_eq!(ordered_order, ["slow", "fast"]);
assert_eq!(
ordered_peak, 1,
"explicit async=false scripts must retain their insertion-order queue"
ordered_peak, 2,
"explicit async=false scripts must overlap fetches while retaining their execution-order queue"
);
assert!(
async_elapsed_ms + 50 < ordered_elapsed_ms,
"concurrent fetches should avoid the ordered waterfall: async={async_elapsed_ms}ms ordered={ordered_elapsed_ms}ms"
ordered_elapsed_ms < async_elapsed_ms + 300,
"execution ordering must not serialize network fetches: async={async_elapsed_ms}ms ordered={ordered_elapsed_ms}ms"
);
}
+44 -19
View File
@@ -274,28 +274,44 @@ function _decodeDataScriptUrl(url) {
globalThis.__markParserScripts = function(nids) {
for (const nid of nids || []) Deno.core.ops.op_script_mark_started(+nid);
};
async function __fetchDynClassicScript(task) {
let body;
if (task.url.startsWith('data:')) {
body = _decodeDataScriptUrl(task.url);
} else {
const raw = await Deno.core.ops.op_fetch_url(
task.url, "GET", "{}", "", task.pageOrigin, "no-cors", "same-origin"
);
const parsed = JSON.parse(raw);
// The HTML script-fetch algorithm treats an unsuccessful HTTP response
// as a network error. Evaluating its response body is both observably
// unlike browsers and dangerous: JSON error payloads and diagnostic HTML
// must never become script source.
if (!(parsed.status >= 200 && parsed.status <= 299)) {
throw new Error('HTTP ' + (parsed.status || 0));
}
body = parsed.body;
}
return body;
}
function __startDynClassicFetch(task) {
// Attach both reactions immediately. An in-order script may finish fetching
// before an earlier queue member; retaining a settled value avoids an
// unhandled-rejection report while its execution turn is still blocked.
task.fetchResult = __fetchDynClassicScript(task).then(
body => ({ body }),
error => ({ error }),
);
}
async function __runDynScriptTask(task) {
try {
if (task.isModule) {
await import(task.url);
} else {
let body;
if (task.url.startsWith('data:')) {
body = _decodeDataScriptUrl(task.url);
} else {
const raw = await Deno.core.ops.op_fetch_url(
task.url, "GET", "{}", "", task.pageOrigin, "no-cors", "same-origin"
);
const parsed = JSON.parse(raw);
// The HTML script-fetch algorithm treats an unsuccessful HTTP
// response as a network error. Evaluating its response body is both
// observably unlike browsers and dangerous: JSON error payloads and
// diagnostic HTML must never become script source.
if (!(parsed.status >= 200 && parsed.status <= 299)) {
throw new Error('HTTP ' + (parsed.status || 0));
}
body = parsed.body;
}
if (!task.fetchResult) __startDynClassicFetch(task);
const fetched = await task.fetchResult;
if (fetched.error) throw fetched.error;
const body = fetched.body;
if (body) {
// A fetched async script is executed by a ScriptRunner task, not by
// the fetch promise's microtask continuation. Besides matching event
@@ -1374,8 +1390,17 @@ function __prepareInsertedScript(script) {
const explicitlyInOrder = !isModule
&& Object.prototype.hasOwnProperty.call(script, 'async')
&& script.async === false;
if (!isModule && !explicitlyInOrder) {
__runAsyncClassicScript(task);
if (!isModule) {
// Fetch all dynamically inserted classics immediately. `async=false`
// changes only execution order: browsers still overlap their network
// requests, then hold a ready body behind earlier ordered scripts.
__startDynClassicFetch(task);
if (explicitlyInOrder) {
__dynScriptQueue.push(task);
__processDynScriptQueue();
} else {
__runAsyncClassicScript(task);
}
} else {
__dynScriptQueue.push(task);
__processDynScriptQueue();