用 fetch 发请求现在很常见,但很多人一开始都会踩同一个坑:以为网络出问题或者接口报错时,fetch 会自动进 .catch。其实不是这样。
fetch 并不总是抛错
比如你发了个请求,服务器返回了 404 或 500,这时候 fetch 的 Promise 并不会被拒绝。只有在真正“网络异常”的时候,比如断网、DNS 解析失败、请求根本发不出去,才会触发 catch。
这意味着,哪怕后端崩了返回 500,只要请求能通,fetch 就算“成功”了。真正的错误处理得你自己判断。
怎么正确判断响应是否出错?
你需要检查响应的 ok 属性。这个属性为 true 时表示状态码是 2xx,否则就是有问题。
fetch('/api/user')
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(data => {
console.log(data);
})
.catch(err => {
console.error('Fetch failed: ', err);
});
也可以用 status 判断具体问题
有时候你想区分是 404 还是 500,可以自己根据 status 做不同提示。比如用户访问了一个不存在的页面,你可以提醒“内容没找到”,而如果是服务器内部错误,就提示“系统开小差了”。
fetch('/api/post/123')
.then(res => {
if (res.status === 404) {
showNotFound();
} else if (res.status >= 500) {
showServerError();
} else if (res.ok) {
return res.json();
}
})
.then(data => {
if (data) updateUI(data);
});
别忘了网络异常的情况
虽然上面处理了 HTTP 状态,但真遇到断网或请求超时,就得靠 catch 来兜底。可以给用户一个友好提示,比如“网络不给力,请检查连接”。
加上超时控制更稳妥
fetch 默认没有超时,如果服务器一直不回,页面就卡着。可以自己加个限时逻辑:
const controller = new AbortController();
// 5 秒后中断请求
setTimeout(() => controller.abort(), 5000);
fetch('/api/slow-data', { signal: controller.signal })
.then(res => {
if (!res.ok) throw new Error('Bad response');
return res.json();
})
.catch(err => {
if (err.name === 'AbortError') {
console.log('Request timed out');
} else {
console.error('Error: ', err);
}
});
这种写法适合那些对响应速度敏感的功能,比如搜索建议、实时状态更新。
实际开发中的小技巧
可以把通用的错误处理封装成函数,避免每个请求都重复写。比如写个 safeFetch,统一处理状态码、JSON 解析和超时。
另外,调试时多看浏览器的 Network 面板,能清楚看到请求发出没有、响应码是多少,比光看控制台快多了。