Repository navigation
[BUG] 长时间的 xhr 请求,特定时间后超时,不会返回响应 #1343
Description
Activity
Xhr是很老舊的設計
你改用fetch吧參數加 fetch: true
@enncy 打勾这个试试看?

@enncy 打勾这个试试看?
和这个没关系;应该是一个超长的xhr请求,sw没有任何活跃,就被浏览器关闭sw了,然后导致了这个问题;可以考虑给这种情况价格ping/pong?让sw活跃一下
@enncy 打勾这个试试看?

和这个没关系;应该是一个超长的xhr请求,sw没有任何活跃,就被浏览器关闭sw了,然后导致了这个问题;可以考虑给这种情况价格ping/pong?让sw活跃一下
测试用的服务端接口已添加:
GET https://test-case.ggnb.top/delay?seconds=60,会等待指定秒数后返回 JSON 响应,用于模拟长时间请求。以下是用于验证该问题的测试脚本,可直接在脚本猫中安装运行:
// ==UserScript== // @name 测试长时间XHR请求 (#1343) // @namespace test-long-xhr-1343 // @version 1.0.0 // @description 验证超过 Service Worker 空闲超时(~30s)的长请求是否能正常收到响应 // @match *://*/* // @grant GM_xmlhttpRequest // @connect test-case.ggnb.top // ==/UserScript== (function () { "use strict"; const DELAY_SECONDS = 60; const url = `https://test-case.ggnb.top/delay?seconds=${DELAY_SECONDS}`; console.log(`[#1343] 开始测试: 请求 ${url}`); console.log(`[#1343] 预期等待 ${DELAY_SECONDS} 秒...`); const t0 = Date.now(); GM_xmlhttpRequest({ method: "GET", url, responseType: "json", onload: function (res) { const elapsed = ((Date.now() - t0) / 1000).toFixed(1); console.log(`[#1343] onload: status=${res.status}, 耗时=${elapsed}s`); console.log(`[#1343] 响应体:`, res.response); if (res.status === 200 && res.response && res.response.delayed === DELAY_SECONDS) { console.log(`[#1343] ✅ 测试通过: 长时间请求(${elapsed}s)正常收到响应`); } else { console.log(`[#1343] ❌ 测试失败: status=${res.status}, response=${JSON.stringify(res.response)}`); } }, onerror: function (res) { const elapsed = ((Date.now() - t0) / 1000).toFixed(1); console.log(`[#1343] ❌ onerror: 耗时=${elapsed}s, status=${res.status}`); console.log(`[#1343] 测试失败: 请求发生错误`); }, ontimeout: function (res) { const elapsed = ((Date.now() - t0) / 1000).toFixed(1); console.log(`[#1343] ❌ ontimeout: 耗时=${elapsed}s`); console.log(`[#1343] 测试失败: 请求超时`); }, }); })();
使用方法:
- 在脚本猫中安装上面的脚本
- 打开任意网页,等待约 60 秒
- 查看 F12 控制台输出
预期结果:
- 如果长时间请求能正常收到响应,控制台会显示
✅ 测试通过 - 如果 Service Worker 空闲超时导致请求丢失,控制台不会收到 onload 回调(或者收到 onerror/ontimeout)
可以通过修改
DELAY_SECONDS变量来测试不同的延迟时间(如 30、60、90、180 秒)。- addedexternalsomething related to browser bugsomething related to browser bugand removedunable reproduce没有复现方式/无法复现没有复现方式/无法复现
on May 30, 2026 测试脚本
// ==UserScript== // @name 测试长时间XHR请求 (#1343) // @namespace test-long-xhr-1343 // @version 1.0.0 // @description 验证超过 Service Worker 空闲超时(~30s)的长请求是否能正常收到响应 // @match *://*/* // @grant GM_xmlhttpRequest // @connect test-case.ggnb.top // @connect httpbun.com // ==/UserScript== (function () { "use strict"; const DELAY_SECONDS = 35; const TIMEOUT_SECONDS = 40; const UI_INTERVAL_MS = 250; // const url = `https://test-case.ggnb.top/delay?seconds=${DELAY_SECONDS}`; const url = `https://httpbun.com/delay/${DELAY_SECONDS}`; const COLOR_WAITING = "#2563eb"; const COLOR_DUE = "#f59e0b"; const COLOR_TIMEOUT = "#dc2626"; // -------- Utility Functions ----------- // Create an element, apply styles (string or array), set text/html, and append to a parent function createEl(tag, styles, props = {}, parent = null) { const el = document.createElement(tag); if (styles) el.style.cssText = Array.isArray(styles) ? styles.join(";") : styles; Object.assign(el, props); if (parent) parent.append(el); return el; } function makeCard(label, value, parent) { const card = createEl("div", "background:rgba(0,0,0,0.25);border:1px solid rgba(255,255,255,0.12);border-radius:8px;padding:8px;min-width:0", {}, parent); createEl("div", "font-size:11px;color:rgba(255,255,255,0.65);margin-bottom:2px", { textContent: label }, card); const valueEl = createEl("div", "font-size:16px;font-weight:800;color:#fff;overflow:hidden;text-overflow:ellipsis;white-space:nowrap", { textContent: value }, card); return { card, valueEl }; } // -------- Setup Visual Logger DOM ----------- const box = createEl("div", [ "position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);z-index:2147483647", "width:min(760px,calc(100vw - 32px));max-height:78vh;overflow:auto;padding:16px;box-sizing:border-box", "background:rgba(0,0,0,0.9);color:#fff;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace", "font-size:13px;line-height:1.5;border:1px solid rgba(255,255,255,0.25);border-radius:12px;box-shadow:0 12px 40px rgba(0,0,0,0.45);white-space:pre-wrap;word-break:break-word" ]); createEl("div", "font-weight:700;font-size:15px;margin-bottom:12px;padding-bottom:8px;border-bottom:1px solid rgba(255,255,255,0.2)", { textContent: "Long XHR Request Test #1343" }, box); const ui = createEl("div", "background:rgba(255,255,255,0.08);border:1px solid rgba(255,255,255,0.14);border-radius:10px;padding:12px;margin-bottom:12px", {}, box); const statusRow = createEl("div", "display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:10px;flex-wrap:wrap", {}, ui); const elapsedBig = createEl("div", "font-size:24px;font-weight:800;color:#fff", { textContent: "0.0s elapsed" }, statusRow); const progressOuter = createEl("div", "position:relative;height:22px;background:rgba(255,255,255,0.14);border-radius:999px;overflow:hidden;border:1px solid rgba(255,255,255,0.18);margin-bottom:8px", {}, ui); const progressInner = createEl("div", `height:100%;width:0%;background:${COLOR_WAITING};transition:width 0.2s linear, background-color 0.2s linear`, {}, progressOuter); createEl("div", `position:absolute;top:0;bottom:0;left:${Math.min(100, (DELAY_SECONDS / TIMEOUT_SECONDS) * 100)}%;width:3px;background:#fff;box-shadow:0 0 8px rgba(255,255,255,0.9)`, { title: `Expected response at ${DELAY_SECONDS}s` }, progressOuter); const scaleRow = createEl("div", "display:flex;justify-content:center;gap:8px;font-size:12px;color:rgba(255,255,255,0.75);margin-bottom:12px", {}, ui); [`Start: 0s`, `/`, `Expected: ${DELAY_SECONDS}s`, `/`, `Timeout: ${TIMEOUT_SECONDS}s`].forEach(text => { createEl("span", null, { textContent: text }, scaleRow); }); const infoGrid = createEl("div", "display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px;margin-bottom:10px", {}, ui); const elapsedCard = makeCard("Elapsed", "0.0s", infoGrid); makeCard("Expected", `${DELAY_SECONDS}s`, infoGrid); makeCard("Timeout", `${TIMEOUT_SECONDS}s`, infoGrid); const hint = createEl("div", "font-size:12px;color:rgba(255,255,255,0.78)", { textContent: `Awaiting response (expected ~${DELAY_SECONDS}s). Request times out at ${TIMEOUT_SECONDS}s.` }, ui); createEl("div", "font-weight:700;font-size:13px;margin:10px 0 6px;color:rgba(255,255,255,0.9)", { textContent: "Logs" }, box); const logArea = createEl("div", "max-height:260px;overflow:auto;padding-top:6px;border-top:1px solid rgba(255,255,255,0.12)", {}, box); const log = (...args) => { const content = args.map(arg => { if (typeof arg === "string") return arg; try { return JSON.stringify(arg, null, 2); } catch (_) { return String(arg); } }).join(" "); createEl("div", "margin:2px 0;", { textContent: content }, logArea); logArea.scrollTop = logArea.scrollHeight; box.scrollTop = box.scrollHeight; }; const prefixLog = (msg, ...args) => log(`[#1343] ${msg}`, ...args); const mount = () => (document.body || document.documentElement).appendChild(box); if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", mount, { once: true }); } else { mount(); } // -------- UI State Helpers ----------- const t0 = Date.now(); let uiTimer, finished = false; const getElapsedSeconds = () => (Date.now() - t0) / 1000; const STATUS_MAP = { running: `Awaiting response (expected ~${DELAY_SECONDS}s). Request times out at ${TIMEOUT_SECONDS}s.`, success: "Success — response received before the timeout.", fail: "Completed, but the response didn't match the expected result.", timeout: "Timed out — no valid response within the time limit.", error: "Failed — a network or script error occurred." }; function setStatus(type) { if (STATUS_MAP[type]) hint.textContent = STATUS_MAP[type]; } function updateVisualUI() { const elapsed = getElapsedSeconds(); const elapsedFixed = elapsed.toFixed(1); const pct = Math.min(100, (elapsed / TIMEOUT_SECONDS) * 100); elapsedBig.textContent = `${elapsedFixed}s elapsed`; elapsedCard.valueEl.textContent = `${elapsedFixed}s`; progressInner.style.width = `${pct}%`; progressInner.style.background = elapsed >= TIMEOUT_SECONDS ? COLOR_TIMEOUT : elapsed >= DELAY_SECONDS ? COLOR_DUE : COLOR_WAITING; if (!finished) { if (elapsed >= TIMEOUT_SECONDS) { setStatus("timeout"); } else if (elapsed >= DELAY_SECONDS) { setStatus("running"); hint.textContent = `Past the expected response time. Waiting until timeout at ${TIMEOUT_SECONDS}s.`; } else { hint.textContent = `Response - expected in ~${Math.max(0, DELAY_SECONDS - elapsed).toFixed(1)}s | Timeout in ~${Math.max(0, TIMEOUT_SECONDS - elapsed).toFixed(1)}s`; } } } uiTimer = setInterval(updateVisualUI, UI_INTERVAL_MS); updateVisualUI(); // -------- Execution & Network Request ----------- try { prefixLog(`测试开始 - 请求地址: ${url}`); prefixLog(`预期响应时间: 约 ${DELAY_SECONDS} 秒`); prefixLog(`超时阈值: ${TIMEOUT_SECONDS} 秒`); const finish = (statusType) => { finished = true; if (uiTimer) clearInterval(uiTimer); updateVisualUI(); setStatus(statusType); progressInner.style.width = `${Math.min(100, (getElapsedSeconds() / TIMEOUT_SECONDS) * 100)}%`; }; const handleXhr = (type, res) => { try { const elapsed = getElapsedSeconds().toFixed(1); if (type === "load") { prefixLog(`收到响应 (onload) - 状态码: ${res.status} / 耗时: ${elapsed}s`); const r = JSON.stringify(res.response); prefixLog(`响应内容:`, r); if (res.status === 200) { finish("success"); prefixLog(`✅ 测试通过 - 长请求 (${elapsed}s) 成功返回,未受 Service Worker 空闲超时影响`); } else { finish("fail"); prefixLog(`❌ 测试失败 - 状态码: ${res.status} / 响应: ${r}`); } } else if (type === "error") { finish("error"); prefixLog(`❌ 请求出错 (onerror) - 耗时: ${elapsed}s / 状态码: ${res && res.status}`); prefixLog(`测试失败 - 请求过程中发生网络或脚本错误`); } else { finish("timeout"); prefixLog(`❌ 请求超时 (ontimeout) - 耗时: ${elapsed}s`); prefixLog(`测试失败 - 在 ${TIMEOUT_SECONDS}s 内未收到有效响应`); } } catch (e) { finish("error"); prefixLog(`❌ 处理响应时发生异常:`, { error: JSON.stringify(e?.message || e || "") }); } }; GM_xmlhttpRequest({ method: "GET", url, responseType: "json", timeout: TIMEOUT_SECONDS * 1000, onload: (res) => handleXhr("load", res), onerror: (res) => handleXhr("error", res), ontimeout: () => handleXhr("timeout") }); } catch (e) { finished = true; if (uiTimer) clearInterval(uiTimer); setStatus("error"); prefixLog(`❌ 脚本执行异常:`, { error: JSON.stringify(e?.message || e || "") }); } })();
@CodFrm 你能重视吗?如果不能就关这个 Issue.能重視
能重视的话,应视作为 browser bug,而不是搞 ping.
没理解你意思 这不是 重现了么,ping能解决这个问题么?
浏览器问题
暂时解决方法:使用
GM_wakeup@grant加入GM_setValue- 加入
// @require https://scriptcat.org/lib/6455/1.0.2/GM_wakeup.js?sha384-rllZRmN/BkHxStnHOajbDjYcCDvWMZtr1oUG0LxAJ9CxjzWGRCIK9ZNOXNJ+Fg31 - xhr 开始前加入
GM_wakeup.start() - xhr 结束时加入
GM_wakeup.stop()
测试 & 结果
- 注: 如果浏览器/网页不在前台,浏览器内部也会节能而导致无回应。
https://test-case.ggnb.top/delay?seconds=设 180s 的话会返回 504 Gateway Timeout
// ==UserScript== // @name 测试长时间XHR请求 (#1343) // @namespace test-long-xhr-1343 // @version 1.0.0-wakeup // @description 验证超过 Service Worker 空闲超时(~30s)的长请求是否能正常收到响应 // @match *://*/* // @grant GM_xmlhttpRequest // @grant GM_setValue // @connect test-case.ggnb.top // @connect httpbun.com // @require https://scriptcat.org/lib/6455/1.0.2/GM_wakeup.js?sha384-rllZRmN/BkHxStnHOajbDjYcCDvWMZtr1oUG0LxAJ9CxjzWGRCIK9ZNOXNJ+Fg31 // ==/UserScript== (function () { "use strict"; const DELAY_SECONDS = 180; const TIMEOUT_SECONDS = 200; const UI_INTERVAL_MS = 250; // const url = `https://test-case.ggnb.top/delay?seconds=${DELAY_SECONDS}`; const url = `https://httpbun.com/delay/${DELAY_SECONDS}`; const COLOR_WAITING = "#2563eb"; const COLOR_DUE = "#f59e0b"; const COLOR_TIMEOUT = "#dc2626"; // -------- Utility Functions ----------- // Create an element, apply styles (string or array), set text/html, and append to a parent function createEl(tag, styles, props = {}, parent = null) { const el = document.createElement(tag); if (styles) el.style.cssText = Array.isArray(styles) ? styles.join(";") : styles; Object.assign(el, props); if (parent) parent.append(el); return el; } function makeCard(label, value, parent) { const card = createEl("div", "background:rgba(0,0,0,0.25);border:1px solid rgba(255,255,255,0.12);border-radius:8px;padding:8px;min-width:0", {}, parent); createEl("div", "font-size:11px;color:rgba(255,255,255,0.65);margin-bottom:2px", { textContent: label }, card); const valueEl = createEl("div", "font-size:16px;font-weight:800;color:#fff;overflow:hidden;text-overflow:ellipsis;white-space:nowrap", { textContent: value }, card); return { card, valueEl }; } // -------- Setup Visual Logger DOM ----------- const box = createEl("div", [ "position:fixed;left:50%;top:50%;transform:translate(-50%,-50%);z-index:2147483647", "width:min(760px,calc(100vw - 32px));max-height:78vh;overflow:auto;padding:16px;box-sizing:border-box", "background:rgba(0,0,0,0.9);color:#fff;font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace", "font-size:13px;line-height:1.5;border:1px solid rgba(255,255,255,0.25);border-radius:12px;box-shadow:0 12px 40px rgba(0,0,0,0.45);white-space:pre-wrap;word-break:break-word" ]); createEl("div", "font-weight:700;font-size:15px;margin-bottom:12px;padding-bottom:8px;border-bottom:1px solid rgba(255,255,255,0.2)", { textContent: "Long XHR Request Test #1343" }, box); const ui = createEl("div", "background:rgba(255,255,255,0.08);border:1px solid rgba(255,255,255,0.14);border-radius:10px;padding:12px;margin-bottom:12px", {}, box); const statusRow = createEl("div", "display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:10px;flex-wrap:wrap", {}, ui); const elapsedBig = createEl("div", "font-size:24px;font-weight:800;color:#fff", { textContent: "0.0s elapsed" }, statusRow); const progressOuter = createEl("div", "position:relative;height:22px;background:rgba(255,255,255,0.14);border-radius:999px;overflow:hidden;border:1px solid rgba(255,255,255,0.18);margin-bottom:8px", {}, ui); const progressInner = createEl("div", `height:100%;width:0%;background:${COLOR_WAITING};transition:width 0.2s linear, background-color 0.2s linear`, {}, progressOuter); createEl("div", `position:absolute;top:0;bottom:0;left:${Math.min(100, (DELAY_SECONDS / TIMEOUT_SECONDS) * 100)}%;width:3px;background:#fff;box-shadow:0 0 8px rgba(255,255,255,0.9)`, { title: `Expected response at ${DELAY_SECONDS}s` }, progressOuter); const scaleRow = createEl("div", "display:flex;justify-content:center;gap:8px;font-size:12px;color:rgba(255,255,255,0.75);margin-bottom:12px", {}, ui); [`Start: 0s`, `/`, `Expected: ${DELAY_SECONDS}s`, `/`, `Timeout: ${TIMEOUT_SECONDS}s`].forEach(text => { createEl("span", null, { textContent: text }, scaleRow); }); const infoGrid = createEl("div", "display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px;margin-bottom:10px", {}, ui); const elapsedCard = makeCard("Elapsed", "0.0s", infoGrid); makeCard("Expected", `${DELAY_SECONDS}s`, infoGrid); makeCard("Timeout", `${TIMEOUT_SECONDS}s`, infoGrid); const hint = createEl("div", "font-size:12px;color:rgba(255,255,255,0.78)", { textContent: `Awaiting response (expected ~${DELAY_SECONDS}s). Request times out at ${TIMEOUT_SECONDS}s.` }, ui); createEl("div", "font-weight:700;font-size:13px;margin:10px 0 6px;color:rgba(255,255,255,0.9)", { textContent: "Logs" }, box); const logArea = createEl("div", "max-height:260px;overflow:auto;padding-top:6px;border-top:1px solid rgba(255,255,255,0.12)", {}, box); const log = (...args) => { const content = args.map(arg => { if (typeof arg === "string") return arg; try { return JSON.stringify(arg, null, 2); } catch (_) { return String(arg); } }).join(" "); createEl("div", "margin:2px 0;", { textContent: content }, logArea); logArea.scrollTop = logArea.scrollHeight; box.scrollTop = box.scrollHeight; }; const prefixLog = (msg, ...args) => log(`[#1343] ${msg}`, ...args); const mount = () => (document.body || document.documentElement).appendChild(box); if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", mount, { once: true }); } else { mount(); } // -------- UI State Helpers ----------- const t0 = Date.now(); let uiTimer, finished = false; const getElapsedSeconds = () => (Date.now() - t0) / 1000; const STATUS_MAP = { running: `Awaiting response (expected ~${DELAY_SECONDS}s). Request times out at ${TIMEOUT_SECONDS}s.`, success: "Success — response received before the timeout.", fail: "Completed, but the response didn't match the expected result.", timeout: "Timed out — no valid response within the time limit.", error: "Failed — a network or script error occurred." }; function setStatus(type) { if (STATUS_MAP[type]) hint.textContent = STATUS_MAP[type]; } function updateVisualUI() { const elapsed = getElapsedSeconds(); const elapsedFixed = elapsed.toFixed(1); const pct = Math.min(100, (elapsed / TIMEOUT_SECONDS) * 100); elapsedBig.textContent = `${elapsedFixed}s elapsed`; elapsedCard.valueEl.textContent = `${elapsedFixed}s`; progressInner.style.width = `${pct}%`; progressInner.style.background = elapsed >= TIMEOUT_SECONDS ? COLOR_TIMEOUT : elapsed >= DELAY_SECONDS ? COLOR_DUE : COLOR_WAITING; if (!finished) { if (elapsed >= TIMEOUT_SECONDS) { setStatus("timeout"); } else if (elapsed >= DELAY_SECONDS) { setStatus("running"); hint.textContent = `Past the expected response time. Waiting until timeout at ${TIMEOUT_SECONDS}s.`; } else { hint.textContent = `Response - expected in ~${Math.max(0, DELAY_SECONDS - elapsed).toFixed(1)}s | Timeout in ~${Math.max(0, TIMEOUT_SECONDS - elapsed).toFixed(1)}s`; } } } uiTimer = setInterval(updateVisualUI, UI_INTERVAL_MS); updateVisualUI(); // -------- Execution & Network Request ----------- try { prefixLog(`测试开始 - 请求地址: ${url}`); prefixLog(`预期响应时间: 约 ${DELAY_SECONDS} 秒`); prefixLog(`超时阈值: ${TIMEOUT_SECONDS} 秒`); const finish = (statusType) => { finished = true; if (uiTimer) clearInterval(uiTimer); updateVisualUI(); setStatus(statusType); progressInner.style.width = `${Math.min(100, (getElapsedSeconds() / TIMEOUT_SECONDS) * 100)}%`; }; const handleXhr = (type, res) => { try { const elapsed = getElapsedSeconds().toFixed(1); GM_wakeup.stop(); if (type === "load") { prefixLog(`收到响应 (onload) - 状态码: ${res.status} / 耗时: ${elapsed}s`); const r = JSON.stringify(res.response); prefixLog(`响应内容:`, r); if (res.status === 200) { finish("success"); prefixLog(`✅ 测试通过 - 长请求 (${elapsed}s) 成功返回,未受 Service Worker 空闲超时影响`); } else { finish("fail"); prefixLog(`❌ 测试失败 - 状态码: ${res.status} / 响应: ${r}`); } } else if (type === "error") { finish("error"); prefixLog(`❌ 请求出错 (onerror) - 耗时: ${elapsed}s / 状态码: ${res && res.status}`); prefixLog(`测试失败 - 请求过程中发生网络或脚本错误`); } else { finish("timeout"); prefixLog(`❌ 请求超时 (ontimeout) - 耗时: ${elapsed}s`); prefixLog(`测试失败 - 在 ${TIMEOUT_SECONDS}s 内未收到有效响应`); } } catch (e) { finish("error"); prefixLog(`❌ 处理响应时发生异常:`, { error: JSON.stringify(e?.message || e || "") }); } }; GM_wakeup.start(); GM_xmlhttpRequest({ method: "GET", url, responseType: "json", timeout: TIMEOUT_SECONDS * 1000, onload: (res) => handleXhr("load", res), onerror: (res) => handleXhr("error", res), ontimeout: () => handleXhr("timeout") }); } catch (e) { finished = true; if (uiTimer) clearInterval(uiTimer); setStatus("error"); prefixLog(`❌ 脚本执行异常:`, { error: JSON.stringify(e?.message || e || "") }); } })();

W3C 例会讨论结果
Issue 1016:Service Worker / Event Page 在进行中 fetch / XHR 时的生命周期管理
问题是长时间网路请求可能因 background service worker 被终止而中断。会议提到之前讨论过的 waitUntil 可能可以解决,而且各浏览器过去都表达过支持。开发者也分享实务上会使用 heartbeat / keepalive hack 延长生命周期。Chrome 补充说 30 秒不是硬性上限,而是 idle 判断;另有 5 分钟 unsupervised activity timeout。下一步是有人提出更明确的 waitUntil 语意提案。
这个Bug之后应该会有所改善。
根据我的测试,暂时在开发者层面无法解决 (60 秒还可以强制唤醒一下,180秒的话,如果浏览器一直在后台不活跃,基本上就是死定了。这个要先等 waitUntil 做好了才能提出来)waitUntil: w3c/webextensions#416



问题描述
Service Worker 有默认的空闲超时(约30秒), 当某个请求需要较长时间处理(例如调用AI),浏览器经过大概几十秒后没有收到响应,就算服务器最终返回了响应,浏览器也无法收到。
重现步骤
上面那段 interval 如果删掉的话,下面如果在 180 秒左右返回数据,浏览器都是收不到的。 定时激活好像才能收到响应。
脚本猫版本
v1.3.1
操作系统以及浏览器信息
win11 chrome 146.0.7680.178
补充信息 (选填)
No response