
浏览器原生不提供 window.requestsOpen 这类全局网络请求计数属性或 networkCallOpen/closed 系统事件,需通过封装 fetch 并结合请求计数器手动实现全局加载态管理。
浏览器原生不提供 `window.requestsopen` 这类全局网络请求计数属性或 `networkcallopen/closed` 系统事件,需通过封装 `fetch` 并结合请求计数器手动实现全局加载态管理。
在现代 Web 应用中,统一展示「全局加载指示器」(如顶部进度条、旋转图标)是常见需求。但直接在每个 fetch 前后调用 showNetworkIcon() / hideNetworkIcon() 会导致竞态问题:当多个请求并发时,先完成的请求可能错误地关闭指示器,而其他请求仍在进行中。
根本原因:浏览器 window 或 document 对象不暴露任何原生属性或事件来反映当前活跃的网络请求数量。FetchEvent 仅存在于 Service Worker 环境;AbortSignal 仅用于单次请求控制,无法跨请求聚合状态。
✅ 正确解法是 封装 fetch,维护一个全局请求计数器,并配合自定义事件通知状态变更:
// 全局请求计数器(初始化为 0)
let activeRequestCount = 0;
// 封装 fetch,自动管理计数与事件
function trackedFetch(url, options = {}) {
// 请求开始:计数 +1,触发 open 事件
activeRequestCount++;
if (activeRequestCount === 1) {
window.dispatchEvent(new CustomEvent('networkCallOpen', { detail: { url } }));
}
// 执行实际请求
return fetch(url, options)
.then(response => {
// 请求成功:计数 -1,若归零则触发 closed 事件
activeRequestCount--;
if (activeRequestCount === 0) {
window.dispatchEvent(new CustomEvent('networkCallClosed'));
}
return response;
})
.catch(error => {
// 请求失败:同样需计数 -1 并检查是否归零
activeRequestCount--;
if (activeRequestCount === 0) {
window.dispatchEvent(new CustomEvent('networkCallClosed'));
}
throw error;
});
}
// 使用示例(无需关心计数逻辑)
trackedFetch('/api/users')
.then(res => res.json())
.then(data => console.log(data));
trackedFetch('/api/posts', { method: 'POST', body: JSON.stringify({ title: 'Hi' }) })
.then(res => res.json());? 关键设计要点:
- ✅ 使用
activeRequestCount实现引用计数,确保只有最后一个请求结束时才触发networkCallClosed; - ✅ 事件派发到
window(而非document),语义更准确,且便于全局监听; - ✅
catch分支必须同步处理计数减一,避免因异常导致计数失准(这是常见陷阱); - ✅ 不依赖第三方库,纯原生 JavaScript,兼容所有现代浏览器。
? 监听与响应(在任意模块中):
// 显示加载指示器
window.addEventListener('networkCallOpen', () => {
document.body.classList.add('loading');
});
// 隐藏加载指示器(仅当所有请求完成)
window.addEventListener('networkCallClosed', () => {
document.body.classList.remove('loading');
});⚠️ 注意事项:
- 此方案不覆盖
XMLHttpRequest—— 若项目仍使用 XHR,需额外封装其open()/onload/onerror; -
fetch的redirect: 'manual'或流式响应(如response.body.getReader())不影响计数逻辑,因计数在fetch()返回 Promise 后即生效; - 在 SPA 路由切换时,建议配合
AbortController取消挂起请求,防止计数泄漏(可扩展为带 Abort 支持的增强版)。
通过该封装,你获得了一个轻量、可靠、可预测的网络请求生命周期管理机制——既解决了多请求竞态问题,又保持了代码的清晰性与可维护性。

















