如何将navigator.sendBeacon转换为fetch调用并模拟其返回值?
如何让带keepalive的fetch模拟sendBeacon的返回逻辑
不少文档会提到,带有keepalive: true的fetch和navigator.sendBeacon功能等效,示例代码如下:
fetch('/track', { method: 'POST', body: getData(), keepalive: true, }); // 被认为等同于👇 navigator.sendBeacon('/track', getData());
但两者的返回逻辑存在明显差异:
sendBeacon返回布尔值,根据MDN描述:
sendBeacon()方法在用户代理成功将数据排队等待传输时返回true,否则返回false。
- 而fetch始终返回
Promise<Response>,关注的是请求的响应结果,而非数据是否成功进入传输队列。
我们可以通过以下方式让fetch实现sendBeacon的“仅判断数据是否排队成功”的返回逻辑:
核心思路
sendBeacon的核心是确认数据已被浏览器加入后台传输队列,不关心后续的响应结果。而fetch的keepalive请求在无法排队时会直接抛出同步错误,成功排队后则进入Promise流程。我们可以利用这一点,通过try/catch捕获排队失败的情况,忽略后续的响应处理,以此返回布尔值。
实现代码
function fetchAsBeacon(url, data) { try { // 发起带keepalive的fetch,匹配sendBeacon的默认行为 fetch(url, { method: 'POST', body: data, keepalive: true, // 不携带凭证(sendBeacon默认行为,需要的话可改为'include') credentials: 'omit', // 禁止自动重定向,减少资源消耗 redirect: 'manual', // 不需要获取响应内容,设置为空避免解析响应 mode: 'no-cors' }).catch(() => {}); // 忽略响应阶段的所有错误,仅关注排队结果 return true; } catch (err) { // 捕获排队失败的错误(如数据超过keepalive大小限制) return false; } } // 使用示例 const isQueued = fetchAsBeacon('/track', getData()); console.log(isQueued); // 输出true或false,与sendBeacon返回逻辑一致
关键细节说明
- 排队失败的捕获:当
keepalive请求的数据大小超过浏览器限制(通常为64KB),或队列已满时,fetch会抛出同步错误,此时我们返回false。 - 匹配sendBeacon行为:设置
credentials: 'omit'、redirect: 'manual'、mode: 'no-cors',让fetch的底层行为更贴近sendBeacon,避免不必要的资源消耗。 - 忽略响应处理:sendBeacon不关心服务器是否响应、响应内容是什么,所以我们不对fetch的Promise做任何响应处理,仅确认请求已成功排队。
注意事项
- 两者在浏览器底层的传输优先级可能存在细微差异,但在埋点、用户行为统计等常见场景下,这种实现可以完全替代sendBeacon的排队判断逻辑。
- 不同浏览器对
keepalive的大小限制可能略有不同,但普遍遵循64KB的规范。
内容的提问来源于stack exchange,提问作者Wallace
相关产品推荐
相关产品推荐

