马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
你是不是也碰到过这种情况:后端小哥信誓旦旦说接口没毛病,你自己用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 字段,否则外卖没送到你还给人打五星好评呢。
🛠️ 来,直接上我项目里在用的“安全代码”
咱们不废话,我先给你看一段可以直接复制的异步哀求封装,然后再拆开说里头的门道。- async function request(url, options = {}) {
- try {
- const response = await fetch(url, options);
- // 重点1:先判断HTTP状态码
- if (!response.ok) {
- // 顺手把服务器返回的错误信息抛出去
- const errorBody = await response.text();
- throw new Error(`请求失败 ${response.status}: ${errorBody}`);
- }
- // 重点2:根据预期类型解析
- const contentType = response.headers.get("content-type");
- if (contentType && contentType.includes("application/json")) {
- return await response.json();
- }
- return await response.text();
- } catch (error) {
- // 重点3:在这儿统一处理上报和提示
- console.error("请求出错:", error);
- throw error; // 继续抛给调用方,别默默吞掉
- }
- }
复制代码 🎯 状态判定肯定要用 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 并行处理处罚。否则你大概会度过一个非常瓦解的调试下战书。
拿我最开始翻车的代码举例吧,当时想批量拉取用户详情,写法大概长如许:- // ❌ 错误示范:forEach里用await,毛用没有
- const userIds = [1, 2, 3];
- const users = [];
- userIds.forEach(async (id) => {
- const res = await fetch(`/api/user/${id}`);
- const data = await res.json();
- users.push(data);
- });
- // 这时候users大概率还是个空数组,因为forEach不等await
- console.log(users); // 惊不惊喜?意不意外?
复制代码 以为如许就能一个个按序次拿到数据,但结果,灵活了。forEach 的回调被标记为 async 后,并不会让外层的同步代码等它实验完,相当于你安排了三个人同时出门服务,还没等他们返来你就直接公布“使命全部完成”了。
厥后学乖了,改成了两种稳妥写法:- // ✅ 按顺序一个个来(适合有依赖的情况)
- for (const id of userIds) {
- const res = await fetch(`/api/user/${id}`);
- const data = await res.json();
- users.push(data);
- }
- // ✅ 或者不关心顺序,全扔出去并行跑
- const promises = userIds.map(id =>
- fetch(`/api/user/${id}`).then(res => res.json())
- );
- const users = await Promise.all(promises);
复制代码 记着了,只要在循环里用 await,老诚实实放弃 forEach,拥抱 for...of,大概直接用 map + Promise.all。这个坑我替你踩过了,别再往里掉啦!!!
好啦,本日掏心窝子的分享就到这儿。假如你在 fetch 上还躺过其他奇希奇怪的坑,大概对我的封装有啥改进发起,特别欢迎给我留言。要是以为这篇唠嗑能帮你省几根头发,动动手辅导个“赞”,别让它在收藏夹吃灰呀~ 🌟
免责声明:如果侵犯了您的权益,请联系站长及时删除侵权内容,谢谢合作!qidao123.com:ToB企服之家,中国第一个企服评测及软件市场,开放入驻,技术点评得现金. |