前端进阶之旅前端进阶之旅
  • 基础篇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 每日动态
  • 公众号动态公众号历史文章
  • 博客动态站长的技术博客
  • 开发者导航常用工具与文档站
首页程序员面试题库Response bodyUsed 只能读一次 clone
浏览浏览器请求与通信 API

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

因为 Response body 是一个只能被消费一次的流,读取后 bodyUsed 变为 true。需要读两次时用 response.clone() 创建副本,但必须发生在读取之前。

前端进阶之旅 · 一题精讲更新于 2026.09.05
浏览器#请求与通信 API#流处理
先看核心答案
理解线索

流绑定与克隆副本

  1. 流只能消费一次读取后流锁定,bodyUsed 置 true。
  2. clone 复制引用clone 在未读时创建两个独立可读流。
  3. 克隆后各自独立原对象和副本均只能读一次。

若已经读取过 response,直接调用 clone 会同步抛出 TypeError,必须先检查 bodyUsed。

核心回答

先记住这个答案

fetch 返回的 Response 对象,其 body 是一个 ReadableStream,数据只能被消费一次。一旦调用 json()、text()、blob() 或直接读取 body,该流便会被锁定或关闭,bodyUsed 从 false 变为 true。再次读取会抛出 TypeError。需要读取两次时,必须在任何读取操作之前调用 response.clone(),得到一个共享底层流的副本,然后分别对原对象和副本各读一次,每次读取都会使对应的 bodyUsed 变为 true。

  • Response body 是流,读取后即被消耗。
  • bodyUsed 只反映是否已读取,不可重置。
  • 需读两次必须提前用 clone() 复制。

底层流决定单次消费

fetch 响应体本质是一个 ReadableStream,数据按数据块(chunk)到达,消费时通过 reader 逐步读出。一旦执行 json()、text() 等方法,浏览器内部会将整个流读入缓冲区并释放对原始流的引用,此时 bodyUsed 被置为 true。这个布尔值不是开关,而是对状态的记录,表示 body 已不可再次访问。

如果强行再次调用读取方法,会触发 TypeError,因为流的内部控制器已经关闭或锁定了。设计这种机制是为了避免内存上的重复缓存,因为很多响应体很大(如视频、大 JSON),逐个缓存副本代价过高。同时又想支持两个消费者时,便提供了 clone():它仅在被克隆的对象尚未读取时有效,将流分叉成两个独立的内部流,但缓冲区的数据只有一份真实存储,两个副本各自读走自己那份。

网关同时校验与透传的取舍

在一个纯前端拦截器里,要统一检查 API 返回的业务状态:先读一次 JSON 判断 code 是否为 200,然后需要把原始响应继续传给调用方用于后续的 blob 下载。输入是同一个 response,如果直接 await response.json() 后再把 response 返回,调用方取到的 bodyUsed 已为 true,再想读就崩了。此时必须先 const cloned = response.clone(),用 cloned.json() 做校验,原响应保留给下游消费。

校验通过后,下游读取原始响应时 bodyUsed 仍为 false,完全合法。但要注意,克隆也占用了内存,不可能无限克隆;而且克隆必须在读取前,如果误把 cloned 后面的读取顺序调换,原响应也会被锁定。实践中可将这段逻辑封装成中间件,用 bodyUsed 作防御性检查,明确返回错误而不是运行时异常。

克隆时机与消耗成本

clone() 最大的前提是原始对象尚未读取。如果在 json() 之后调用,会立即抛出 TypeError。此外,即使克隆成功,每个克隆体都只能消费一次,不存在一个读完另一个还能读同一份的语义——它们各自消耗自己的流。对于大体积的二进制响应,克隆并不会复制数据体,但会复制流的内部状态,增加内存和 CPU 开销,因此不可滥用。

当需要读取两次且无法提前克隆(比如 response 已经流转过一次)时,唯一可靠的办法是先把内容读成 ArrayBuffer 或 Blob,再用 blob.slice() 或手动构造新 Response 来“复制”,但这等于在应用层额外缓存整个正文,只适合数据量可控的场景。判断能否克隆,直接看 response.bodyUsed,为 false 才安全。

回答前,多想一步

容易答错的地方

认为 bodyUsed 可以重置
有些人试图通过给 response 重新赋值或调用某些方法把 bodyUsed 改回 false,这是不可能的。bodyUsed 是只读属性,由内部流状态决定,重置行为不存在。正确方式永远是克隆或提前缓存内容。
误以为 clone 后能读多次
clone 产生的每个 Response 仍然只能读一次,clone 的目的是同时提供两个独立的消费通道,而不是让一个响应无限次读取。如果试图对已消费的克隆再次读取,同样会抛出异常。
试着用自己的话回答

面试官还会怎么问?

如果已经读了一次但还没读完,能 clone 吗?

不能。只要读取动作开始,bodyUsed 就变为 true,即使是通过 getReader() 读取但没读完,也认为 body 已被使用,clone 会抛错。

克隆后的响应会影响网络缓存吗?

不会。clone 只操作内存中的流,不会触发新的网络请求,与 HTTP 缓存无关。它只是复制了对相同响应数据的访问路径。

Service Worker 里经常 clone,这和普通页面有区别吗?

原理相同,但 Service Worker 中响应常需要转发给页面并同时本地缓存,因此 clone 是标准用法。要留意 clone 的时机必须在放入缓存和返回页面之前,且每个副本只能被消费一次。

从一道题,走向一组知识

把知识连起来

请求与通信 API

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

同属「请求与通信 API」专题,接着看 fetch HTTP 错误不 reject 处理 在具体场景中的处理方式。

请求与通信 API

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

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

参考资料

  • Response: bodyUsed property

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

本题目录
  1. 先记住这个答案
  2. 底层流决定单次消费
  3. 网关同时校验与透传的取舍
  4. 克隆时机与消耗成本
  5. 容易答错的地方
  6. 面试官还会怎么问
  7. 把知识连起来
读懂,再试着讲出来

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

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