Fetch ordered dynamic scripts concurrently
This commit is contained in:
1 parent
3db63d9125
commit
99ff0601bf
2 files changed
+40
-15
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"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Vendored
+35
-10
@@ -274,11 +274,7 @@ function _decodeDataScriptUrl(url) {
|
||||
globalThis.__markParserScripts = function(nids) {
|
||||
for (const nid of nids || []) Deno.core.ops.op_script_mark_started(+nid);
|
||||
};
|
||||
async function __runDynScriptTask(task) {
|
||||
try {
|
||||
if (task.isModule) {
|
||||
await import(task.url);
|
||||
} else {
|
||||
async function __fetchDynClassicScript(task) {
|
||||
let body;
|
||||
if (task.url.startsWith('data:')) {
|
||||
body = _decodeDataScriptUrl(task.url);
|
||||
@@ -287,15 +283,35 @@ async function __runDynScriptTask(task) {
|
||||
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.
|
||||
// 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 {
|
||||
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) {
|
||||
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();
|
||||
|
||||
Reference in new issue
Block a user