调用response.json()后response.body被锁定的原因及解锁方法(以便调用cancel())
调用response.json()后response.body被锁定的原因及解锁方法(以便调用cancel())
嘿,我来给你把这个问题讲明白哈!
首先说为啥response.body会显示locked: true——这是Fetch API的设计规则:响应体(也就是body对应的可读流)只能被「消费」一次。当你调用response.json()的时候,这个方法会把整个响应体的内容全部读取出来,然后自动关闭这个流,所以之后这个body就被锁定了,没法再对它进行任何读取或操作了。
接下来你关心的怎么「解锁」或者说怎么调用cancel(),得分两种情况来看:
1. 如果你想取消整个请求(而不是操作已读取的响应体)
如果你的需求是在请求还没完成的时候中断它,那不能等拿到response之后再处理,得提前用AbortController来做:
你需要先创建一个AbortController实例,把它的signal参数传给fetch方法,之后只要调用controller.abort()就能随时取消请求。举个例子:
async function getData() { const url = "http://jsonplaceholder.typicode.com/users"; // 创建AbortController实例 const controller = new AbortController(); const { signal } = controller; // 比如模拟3秒后自动取消请求(你也可以在按钮点击等事件里调用controller.abort()) setTimeout(() => controller.abort(), 3000); try { // 把signal传入fetch,绑定请求和控制器 const response = await fetch(url, { signal }); if (!response.ok) throw new Error(`Response status: ${response.status}`); // 只有请求没被取消,才会走到这里读取响应 const json = await response.json(); console.log("获取到的数据:", json); console.log("body是否锁定:", response.body.locked); // 这里会是true,因为json()已经消费了响应体 } catch (error) { // 区分取消请求的错误和其他错误 if (error.name === "AbortError") { console.log("请求已经被取消啦"); } else { console.error("请求出错:", error.message); } } } getData();
这种方式是真正意义上的「取消请求」,而且必须在响应体被消费(比如调用json())之前执行才有效——如果已经拿到了response,说明请求已经完成了,这时候再取消也没用了。
2. 如果你只是想保留原response的body不被锁定
要是你的需求不是取消请求,只是不想让原response的body被锁定,那可以先克隆一份响应,然后消费克隆出来的响应体,原响应的body就不会被锁定了:
async function getData() { const url = "http://jsonplaceholder.typicode.com/users"; try { const response = await fetch(url); if (!response.ok) throw new Error(`Response status: ${response.status}`); // 克隆一份响应 const clonedResponse = response.clone(); // 消费克隆出来的响应体 const json = await clonedResponse.json(); console.log("克隆响应的数据:", json); // 原response的body还没被消费,所以没锁定 console.log("原response.body是否锁定:", response.body.locked); // false } catch (error) { console.error(error.message); } }
不过要注意,这种方法只是保留了原响应体的可操作性,并不能帮你取消已经完成的请求哦。
总结一下
- 锁定原因:
response.json()会一次性读取并关闭响应体流,响应体只能被消费一次,所以之后就锁定了。 - 解决思路:
- 要取消请求:提前用
AbortController绑定fetch,通过controller.abort()中断请求(必须在响应体被消费前执行)。 - 要保留原body不锁定:克隆响应后消费克隆体,原响应的body不会被锁定。
- 要取消请求:提前用
备注:内容来源于stack exchange,提问作者kofifus
相关产品推荐
相关产品推荐

