前端进阶之旅前端进阶之旅
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
  • 基础篇HTML/CSS/JS 打底
  • 进阶篇原理与工程化
  • 高频篇面试最常问的那批
  • 精选篇按模块收敛的总结
  • 手写篇常考代码手写实现
  • 面经篇真实面试问题复盘
  • AI 篇NEWAI 时代的前端考点
  • 历年面经NEW按年份追踪真实考点
  • 每日一题每天一道,攒手感
  • 专项自测100 题快速查漏
  • 小程序题库小程序专项刷题
  • 算法题库NEW在线编码即时判题
  • 知识卡片NEW碎片时间过考点
  • 面试题大全常见问题解析
  • AI 答疑NEW随时提问,即时解析
  • AI 模拟面试NEW模拟真实面试 + 报告
  • AI 定制路线NEW按你的简历现排
  • AI 知识地图NEW串起全站知识点
  • 原理篇React / Vue 源码拆解
  • HTTP从报文一路讲到 HTTPS
  • 浏览器渲染、事件循环、进程
  • 计算机基础Linux、网络、操作系统
  • 设计模式23 种模式怎么用
  • Node学习指南从环境搭建到服务端
  • NPM工作流script、依赖与发布
  • Docker容器化部署上手
  • Canvas图形与动画实战
  • 前端系统进阶学习大型项目工程化
  • 前端综合文章长期沉淀的实践文
  • 思维导图知识点全景图
  • 学习路线按图索骥不跑偏
  • AI 热点NEWAI 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
首页程序员面试题库fetch HTTP 错误不 reject 处理
浏览浏览器请求与通信 API

为什么 fetch 收到 HTTP 404 或 500 响应时 Promise 不会 reject,应该怎么处理?

fetch 把 HTTP 状态码当作成功的响应结果,只在网络层失败时 reject。因此 404、500 会正常 resolve,需要手动检查 response.ok 或 status 并自行抛错。

前端进阶之旅 · 一题精讲更新于 2026.09.05
浏览器#请求与通信 API#异步编程#HTTP
先看核心答案读代码示例
理解线索

reject 与 HTTP 状态无关

  1. 网络层失败断网、DNS 失败、CORS 拦截才会 reject
  2. HTTP 错误状态404、500 仍是成功响应,正常 resolve
  3. 手动检查用 response.ok 或 status 判断后自行抛错

只有没收到任何响应或请求被取消时,fetch 的 Promise 才会 reject。

核心回答

先记住这个答案

fetch 的 Promise 只在网络层失败时 reject,比如 DNS 解析失败、TCP 连接中断、CORS 被拦截、请求被 AbortController 取消。只要服务器返回了响应,哪怕是 404 或 500,Promise 都会 resolve 一个 Response 对象。正确做法是拿到响应后检查 response.ok(status 在 200–299 时为 true)或 response.status,不满足就手动 throw,把 HTTP 错误转成可捕获的异常,再统一在 catch 里处理。

  • fetch 只在网络层失败时 reject
  • 404/500 会正常 resolve Response
  • 用 response.ok 判断后手动 throw

fetch 的 reject 语义只覆盖网络层

fetch 的设计把「请求是否送达并收到响应」和「响应的业务成败」分成两层。Promise 的 resolve 只承诺浏览器拿到了响应头和状态行,此时服务器明确给出了答复,传输层面是成功的。404 表示资源不存在、500 表示服务器内部错误,这些都是应用层语义,fetch 不替开发者裁决它们算不算失败。

真正触发 reject 的情况很有限:URL scheme 非法、DNS 解析失败、连接被拒绝或重置、CORS 预检不通过、调用 AbortController 取消请求。这些情况共同点是没有拿到可用的 Response。判断时可用 response.ok(等价于 status 在 200–299)或直接比较 response.status,然后 throw 一个带状态码的错误。

模拟 fetch 状态检查逻辑JavaScript
function checkResponse(status) {
  const ok = status >= 200 && status <= 299;
  if (!ok) {
    const err = new Error('HTTP ' + status);
    err.status = status;
    throw err;
  }
  return 'resolved';
}

function handle(status) {
  try {
    const r = checkResponse(status);
    return JSON.stringify({ status: status, result: r });
  } catch (e) {
    return JSON.stringify({ status: status, result: 'thrown', code: e.status });
  }
}

console.log(handle(200));
console.log(handle(404));
console.log(handle(500));
查看输出与解释
{"status":200,"result":"resolved"}
{"status":404,"result":"thrown","code":404}
{"status":500,"result":"thrown","code":500}

在纯 JavaScript 环境可运行,演示 response.ok 的判定区间和手动抛错逻辑,与浏览器中检查 Response 的写法一致。

详情页请求不存在商品时静默成功的坑

电商详情页用 fetch 请求 /api/products/123,商品已下架时后端返回 404 和一段 JSON 错误信息。代码写成 fetch(url).then(r => r.json()).then(render),于是 404 的响应被当成正常数据走渲染流程,页面渲染出 undefined 的商品名和价格,没有任何报错提示,线上监控也抓不到异常。

修复方式是在 then 链最前面加判断:if (!response.ok) throw new Error('HTTP ' + response.status),并在 catch 中按 status 区分处理——404 跳转到「商品不存在」页,5xx 显示重试按钮。这样 HTTP 错误从静默失败变成显式分支,监控也能采集到具体状态码。

这样处理有效的原因是它把两层语义重新分开:网络异常和 HTTP 错误都汇聚到 catch,但 catch 里可以通过 error.status 是否存在区分来源。没有 status 的是网络层失败或主动取消,有 status 的是服务器返回的错误,两者给用户的提示文案和重试策略应当不同。

response.ok 判断的失效边界

response.ok 只覆盖 200–299,但有些接口约定 304 也算可用缓存结果,此时 ok 为 false 会把可用响应误判成失败;反过来,也有后端把业务失败包在 200 里,靠 body 中的 code 字段表达,此时 ok 为 true 会把失败误判成成功。这两种情况下只看 ok 都会误判,需要按接口契约补充状态码白名单或 body 层的判断。

另一个边界是 body 解析阶段:即使 ok 为 true,response.json() 也可能因返回了 HTML 错误页或空 body 而 reject,这个 reject 来自解析而非请求。健壮写法是把状态检查和解析错误分别捕获,代价是代码更长,但能给出精确的错误定位,避免把网关的 502 HTML 页误报成「数据格式错误」。

回答前,多想一步

容易答错的地方

以为非 2xx 状态会自动进 catch
fetch 对 404、500 照样 resolve,直接链式调用 r.json() 会把错误响应当正常数据处理。必须在 then 里先检查 response.ok 并手动 throw,错误才会进入 catch 分支。
用 try/catch 包住 await fetch 就觉得万无一失
try/catch 只能捕获 reject 和同步抛错,await fetch 拿到 404 响应时不会抛任何东西。如果不显式检查 ok 并 throw,catch 块对 HTTP 错误永远不会执行。
试着用自己的话回答

面试官还会怎么问?

catch 到的错误怎么区分是网络失败还是被取消的?

被取消时 reject 的是 name 为 AbortError 的 DOMException,网络失败在 Chrome 中是 TypeError: Failed to fetch。判断 e.name === 'AbortError' 即可区分,取消通常不需要提示用户,网络失败则需要。

response.ok 和直接判断 status === 200 有什么区别?

ok 覆盖 200–299 整个区间,包括 201、204 等成功状态;status === 200 会把 204 No Content 误判为失败。除非接口契约明确只有 200 算成功,否则优先用 ok。

可以封装一个自动对 HTTP 错误 reject 的 fetch 吗?

可以,包一层函数检查 ok 并 throw 即可,团队内统一使用能减少遗漏。但要注意保留原始 Response 和 status 供上层判断,且不要吞掉 AbortError 的语义,否则取消逻辑会被误当成请求失败。

从一道题,走向一组知识

把知识连起来

请求与通信 API

为什么 fetch 的 Response body 只能读取一次,需要读两次时怎么办?

同属「请求与通信 API」专题,接着看 Response bodyUsed 只能读一次 clone 在具体场景中的处理方式。

请求与通信 API

fetch 默认跨域请求为什么不带 Cookie,credentials 选项各取值有什么区别?

同属「请求与通信 API」专题,接着看 fetch credentials cookie same-origin include 在具体场景中的处理方式。

参考资料

  • Using the Fetch API

示例用于理解所注明的运行环境与边界;延伸学习可结合原文中的更多案例。

本题目录
  1. 先记住这个答案
  2. fetch 的 reject 语义只覆盖网络层
  3. 详情页请求不存在商品时静默成功的坑
  4. response.ok 判断的失效边界
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

先看核心答案,再读代码。最后展开追问,检查自己有没有遗漏边界。

试着回答追问
浏览全部面试题理解原理,也关注真实的使用场景。回到顶部 ↑