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

+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();