原文:Why Make
fetch's Response Body a Stream?by:T.J. Crowder
Émile Bergeron 在推特上问:为什么 fetch 的响应体(Response body)是流(Stream):
有谁能为我解释一下,将响应体设计成可读流(readable stream)有什么优点吗?
在我看来,将响应体解析成 JSON 应该是幂等的,但是有一个我还没注意到的很好的理由将 API 设计成这样。🤔
@tjcrowder 你知道为什么吗? #JavaScript #wat
const response = await fetch(SOME_URL); response.json(); // fine response.json(); // TypeError: Failed to execute 'json' on 'Response': // body stream is locked
我在这方面不是很专业,但是我一直认为这是出于效率的考虑。我的理由来自以下几个方面,将其设计成流意味着:
- 从理论上来说,浏览器可以避免将整个 body 加载到内存中,然后再进行处理(如解析为 JSON,加载到 ArrayBuffer 等)。相反,根据你调用的方法,浏览器可以直接将响应的网络流传输到 JSON 解析器、ArrayBuffer 构建器、Blob 构建器等。
- 解析等操作可以与读取同时进行(理论上,JSON 中的早期错误将允许它避免继续读取 body)。
Response对象在你消耗它之后不必保留 body 的副本,以防你再次调用json()等;保留结果是使用该响应的代码的工作。
Émile 在推特上指出,你可以 clone 一个 Response,两个 Response 都可以使用 json()。
我知道有一些替代方法可以避免这个错误,比如克隆 Response。
const response = await fetch(SOME_URL); response.clone().json(); // fine! response.json(); // fine as well
…… 这是正确的操作,但是遵循规范(1, 2, 3),我会避免克隆 Response。因为如果我读得正确,至少直到消耗它们,这会读取 body 并存储两份副本(一份用于克隆,一份用于原始副本)。所以我会使用 text() 然后进行两次 JSON.parse:
const response = await fetch(SOME_URL);
if (!response.ok) { // Don't forget the HTTP result check!
throw new Error("HTTP error " + response.status);
}
let text = await response.text();
const copy1 = JSON.parse(text);
const copy2 = JSON.parse(text);
text = null;
我可能错误地理解了 Émile 的具体意图,但是使用流而不是传递大块数据是一件非常流行的事,而且对我来说确保只有 API 的消费者保留数据是有道理的。
Happy Coding!