切标签页时POST请求报错:Uncaught (in promise) TypeError: Failed to fetch
解决方案
1. 页面隐藏时切换回sendBeacon兜底
sendBeacon是浏览器专为页面生命周期变更(如切标签页、卸载)设计的API,比fetch+keepalive更可靠,不会被后台任务限制中断。修改请求逻辑,在页面不可见时优先用它发送数据:
#handleSendEvents = async eventsPayload => { if (this.#isPageVisible) { await this.#activeRequest; } const currentRequestPromise = createControllablePromise(); const cancelTimeout = setTimeout(() => currentRequestPromise.resolve(), 30000); currentRequestPromise.finally(() => clearTimeout(cancelTimeout)); this.#activeRequest = currentRequestPromise; let requestPromise; // 页面不可见时用sendBeacon if (!this.#isPageVisible) { const success = navigator.sendBeacon('*MY_URL*', eventsPayload); requestPromise = Promise.resolve(success); } else { requestPromise = fetch('*MY_URL*', { method: 'POST', headers: { accept: '*/*', 'cache-control': 'no-cache', 'content-type': 'text/plain;charset=UTF-8', }, body: eventsPayload, keepalive: true, mode: 'no-cors', }); } requestPromise.finally(() => { currentRequestPromise.resolve(); }); try { // 页面可见时保留延迟,隐藏时直接等待请求完成 if (this.#isPageVisible) await delay(300); await requestPromise; } catch (e) {} };
2. 修正页面隐藏时的队列中断逻辑
当前setPageVisible直接resolve活跃请求会导致正在处理的请求被中断,改成等待当前请求完成后再推进队列:
setPageVisible = isPageVisible => { this.#isPageVisible = isPageVisible; if (!isPageVisible && this.#activeRequest) { // 等待当前请求结束后,让队列自动处理剩余任务 this.#activeRequest.then(() => { this.#requestQueue = this.#requestQueue.then(() => {}); }); } };
3. 拆分大批次数据,避免keepalive大小限制
浏览器对keepalive请求的body大小有上限(通常64KB),如果20条事件超过这个阈值会直接失败。在发送时拆分批次:
sendEvents = async eventsPayload => { try { const events = JSON.parse(eventsPayload); const batchSize = 5; // 根据单条事件大小调整批次数量 for (let i = 0; i < events.length; i += batchSize) { const batchPayload = JSON.stringify(events.slice(i, i + batchSize)); const newQueue = this.#requestQueue.then(() => this.#handleSendEvents(batchPayload)); this.#requestQueue = newQueue; await newQueue; } } catch (e) { // 非JSON格式直接按原逻辑发送 const newQueue = this.#requestQueue.then(() => this.#handleSendEvents(eventsPayload)); this.#requestQueue = newQueue; return newQueue; } };
关键原因说明
- 页面切到后台时,浏览器会节流非关键任务,
sendBeacon属于浏览器优先级调度任务,能保证完成;而fetch+keepalive优先级低,容易被中断。 keepalive有严格的大小限制,超过阈值会直接触发请求失败。- 原逻辑中强制resolve活跃请求会打断正在进行的请求,导致Pending状态报错。
内容的提问来源于stack exchange,提问作者WebWorker
相关产品推荐
相关产品推荐

