onClick触发Fetch API在Safari报错Load failed问题排查
问题描述
我并非前端方向开发人员,遇到了一个基础的HTML/JavaScript问题,希望得到帮助。
实现需求
朋友的网站基于Wordpress搭建、使用Thrive主题,需要实现点击指定按钮时调用对应API的功能。Thrive主题支持在页脚注入自定义代码,我写了API调用函数,绑定到按钮对应的<a>标签的onclick事件上。
简化后的实现代码:
async function dummy() { var myHeaders = new Headers(); myHeaders.append("Content-Type", "text/plain"); var requestOptions = { method: "POST", headers: myHeaders, body: JSON.stringify({}), redirect: "follow", }; return fetch("some.api.com", requestOptions); } document.getElementById("button1").onclick = async function () { var res = await dummy(); return res.ok; };
故障现象
代码在Chrome浏览器可正常运行,但在Safari及移动端浏览器中运行异常,多数场景下Safari会抛出错误:Unhandled Promise Rejection: TypeError: Load failed。调整过await写法后偶尔能运行成功,但始终无法稳定工作。
注:我清楚当前写法不符合生产环境安全标准,仅为演示场景的快速实现。
故障原因与修复方案
代码存在4个直接导致Safari下运行不稳定的问题,按影响优先级排序:
- 未阻止
<a>标签的默认跳转行为
这是最核心的原因。你绑定点击事件的是a标签,async类型的事件回调执行到await时会暂时让出主线程,Safari会在此时立刻触发a标签的默认行为:如果href是#或空值就触发页面刷新/锚点跳转,如果是其他地址就直接跳走。页面上下文一旦被销毁,还在等待响应的fetch请求会被直接中断,抛出Load failed错误。Chrome对这类场景做了特殊容错,不会立刻销毁上下文中断请求,但Safari、iOS端所有浏览器(包括iOS Chrome、微信Webview)都没有这个容错,因此会大概率失败。 - 请求URL缺少协议前缀
代码里的接口地址写的是some.api.com,没有带https://协议头,Safari不会像Chrome那样自动补全协议,会把该地址识别为当前站点下的相对路径,往Wordpress站点自身域名下拼接这个路径发请求,自然找不到目标接口。 - 无异常捕获逻辑
所有异步请求逻辑都没有加try/catch兜底,只要出现网络波动、请求被拦截、跨域校验失败的情况,就会直接抛出未处理的Promise拒绝错误。 - Content-Type配置与请求体不匹配
代码里设置的Content-Type是text/plain,但实际传的是JSON.stringify序列化后的JSON字符串,后端如果按JSON格式解析参数会直接失败,虽然这个问题不会直接触发前端Load failed报错,但会导致接口业务逻辑无法正常执行,应该改为application/json。
修正后可稳定运行的代码如下:
async function dummy() { const myHeaders = new Headers(); // 修正Content-Type为JSON类型 myHeaders.append("Content-Type", "application/json"); const requestOptions = { method: "POST", headers: myHeaders, body: JSON.stringify({}), redirect: "follow", }; // 补全接口完整协议头,替换为实际接口的全路径地址 return fetch("https://some.api.com/your-actual-api-path", requestOptions); } document.getElementById("button1").onclick = async function (e) { // 阻止a标签默认跳转行为 e.preventDefault(); try { const res = await dummy(); // 此处可添加请求成功后的业务逻辑,比如弹出成功提示 console.log("请求状态:", res.ok); return res.ok; } catch (err) { // 捕获所有请求异常,避免抛出未处理的Promise错误 console.error("请求失败:", err); return false; } };
如果调整后仍偶发失败,优先检查接口的CORS跨域配置:Safari对跨域规则的校验比Chrome严格很多,跨域响应头缺少Access-Control-Allow-Origin、Access-Control-Allow-Methods,或者携带Cookie时未配置Access-Control-Allow-Credentials,都会直接拦截请求抛出相同的Load failed错误。
内容的提问来源于stack exchange,提问作者chris
相关产品推荐
相关产品推荐

