You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

切标签页时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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 06:53:29