聊聊 fetch 利用中我踩过的那些坑和精确打开方式

[复制链接]
发表于 2026-5-26 13:21:03 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

×
你是不是也碰到过这种情况:后端小哥信誓旦旦说接口没毛病,你自己用Postman试了也正常,可前端就是拿不到数据。
控制台一看,报的错驴唇不对马嘴,什么“Failed to fetch”…… 更气人的是,显着写了 .catch(),错误却没捉住,眼睁睁看着页面白屏。
🎯 先说核心:fetch这玩意儿到底坑在哪儿

记得我刚开始写前端那会儿,一听原生的 fetch 比 XMLHttpRequest 好用,立马把项目里的 axios 全换了。结果项目一运行,报错信息差点没把我晃晕。
最核心的一个坑,也是90%新手会掉的——fetch 只把网络错误算作 reject,HTTP错误状态(比如404、500)它还是当乐成处理处罚
换句话说,哪怕服务器返回404,fetch的Promise仍然会走.then(),你要是不手动判定,反面一堆代码就拿着 undefined 开心地实验下去了。
这篇文章不跟你背文档,咱就像朋侪谈天一样,把我这些年对 fetch 的明白、踩过的坑、另有如今不停用的“稳妥写法”,全倒给你。
📦 把fetch想象成点外卖

没错,我以为 fetch 特别像点外卖。你下单(发哀求),平台给你个订单号(Promise),外卖小哥会不会送到你家呢?不愿定,大概商家自己取消订单了,但平台会告诉你“订单已天生”。
fetch 的 Response 对象就是这个“订单详情”。你得自己打开看看内里的 status (200?404?)和 ok 字段,否则外卖没送到你还给人打五星好评呢。
🛠️ 来,直接上我项目里在用的“安全代码”

咱们不废话,我先给你看一段可以直接复制的异步哀求封装,然后再拆开说里头的门道。
  1. async function request(url, options = {}) {
  2.   try {
  3.     const response = await fetch(url, options);
  4.     // 重点1:先判断HTTP状态码
  5.     if (!response.ok) {
  6.       // 顺手把服务器返回的错误信息抛出去
  7.       const errorBody = await response.text();
  8.       throw new Error(`请求失败 ${response.status}: ${errorBody}`);
  9.     }
  10.     // 重点2:根据预期类型解析
  11.     const contentType = response.headers.get("content-type");
  12.     if (contentType && contentType.includes("application/json")) {
  13.       return await response.json();
  14.     }
  15.     return await response.text();
  16.   } catch (error) {
  17.     // 重点3:在这儿统一处理上报和提示
  18.     console.error("请求出错:", error);
  19.     throw error; // 继续抛给调用方,别默默吞掉
  20.   }
  21. }
复制代码
🎯 状态判定肯定要用 response.ok

官方给了个现成属性 response.ok ,相当于 status 在 200-299 之间。千万别本技艺写 if (status === 200) ,我早期吃过亏,碰到 201 Created 这种乐成状态就直接卡死了。
📨 返回值的“一次性消耗”特性

你大概会问,为啥上面我先把错误信息用 text() 读了,表面还能正常 json() ?由于我在抛错误前就已经消耗掉了。
这引出 fetch 最轻易翻车的点——Response 的 .json()、.text()、.blob() 都只能调一次。想既看原始文本又当 JSON 用?不可!要么先 clone() 一份,要么像我如许,只在确定是错误时才读一次。
再说个真实案例:有次我调试接口,想在控制台打印一下原始返回值,于是提前用 await response.json() 把数据拿出来了,接着返回给业务代码再用 .json() 一次,结果直接报错。调试了半天才发现是由于 body 流已经被读完了。
🔐 别忘了剖析的那点鉴戒思

常常有人问我:为啥后端显着返回了JSON,我的代码还是报错?
很多情况是后端在返回 401 或 403 时,固然状态码不对,但 body 还是 JSON 格式的错误信息。假如你不先用 text() 看一眼直接 json(),很大概由于 content-type 不匹配而剖析失败,结果连真正的错误都看不到。
我的风俗是,在非 ok 分支里划一先用 text() 拿到原始内容,既安全,又能把服务器给的提示原样表现出来,用户体验好不少。
🧵 末了啰嗦一句关于async/await

是不是以为写成 async/await 就万事大吉了?我可见过太多同砚在 useEffect 里直接 async,大概 forEach 里用 await,结果数据像没睡醒一样迟迟不来。
假如你在循环里发哀求,老诚实实用 for...of 共同 await,大概用 Promise.all 并行处理处罚。否则你大概会度过一个非常瓦解的调试下战书。
拿我最开始翻车的代码举例吧,当时想批量拉取用户详情,写法大概长如许:
  1. // ❌ 错误示范:forEach里用await,毛用没有
  2. const userIds = [1, 2, 3];
  3. const users = [];
  4. userIds.forEach(async (id) => {
  5.   const res = await fetch(`/api/user/${id}`);
  6.   const data = await res.json();
  7.   users.push(data);
  8. });
  9. // 这时候users大概率还是个空数组,因为forEach不等await
  10. console.log(users); // 惊不惊喜?意不意外?
复制代码
以为如许就能一个个按序次拿到数据,但结果,灵活了。forEach 的回调被标记为 async 后,并不会让外层的同步代码等它实验完,相当于你安排了三个人同时出门服务,还没等他们返来你就直接公布“使命全部完成”了。
厥后学乖了,改成了两种稳妥写法:
  1. // ✅ 按顺序一个个来(适合有依赖的情况)
  2. for (const id of userIds) {
  3.   const res = await fetch(`/api/user/${id}`);
  4.   const data = await res.json();
  5.   users.push(data);
  6. }
  7. // ✅ 或者不关心顺序,全扔出去并行跑
  8. const promises = userIds.map(id =>
  9.   fetch(`/api/user/${id}`).then(res => res.json())
  10. );
  11. const users = await Promise.all(promises);
复制代码
记着了,只要在循环里用 await,老诚实实放弃 forEach,拥抱 for...of,大概直接用 map + Promise.all。这个坑我替你踩过了,别再往里掉啦!!!

好啦,本日掏心窝子的分享就到这儿。假如你在 fetch 上还躺过其他奇希奇怪的坑,大概对我的封装有啥改进发起,特别欢迎给我留言。要是以为这篇唠嗑能帮你省几根头发,动动手辅导个“赞”,别让它在收藏夹吃灰呀~ 🌟

免责声明:如果侵犯了您的权益,请联系站长及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金.
回复

使用道具 举报

登录后关闭弹窗

登录参与点评抽奖  加入IT实名职场社区
去登录
快速回复 返回顶部 返回列表