fetch 分析
问题
const response = await fetch("/data"); // (1)
const data = await response.text(); // (2)
为啥要两次 await, 为什么拆分
解释
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
The fetch() method takes one mandatory argument, the path to the resource you want to fetch. It returns a Promise that resolves to the Response to that request — as soon as the server responds with headers — even if the server response is an HTTP error status.
Once a Response is retrieved, there are a number of methods available to define what the body content is and how it should be handled.
(1) 在 header 有了就 resolve 了 (2) 需要等到 body 整体 resolve
这样设计的好处是可以先读取状态码、headers 等信息,决定后续如何处理
- text() - 文本格式
- json() - JSON 格式
- blob() - 二进制数据
- formData() - 表单数据
try {
const response = await fetch("/api/data");
// 可以立即检查状态码
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
// 检查 content-type
const contentType = response.headers.get("content-type");
if (contentType && contentType.includes("application/json")) {
const data = await response.json(); // 解析 JSON
} else {
const text = await response.text(); // 作为文本处理
}
} catch (error) {
console.error("获取数据失败:", error);
}
https://github.com/sedationh/demo-fetch 这里写了点例子, 除了测试 header 和 body 分离外,还搞了流式获取
Chat With GPT
Note
res.writeHead(200, {
"Content-Type": "application/json",
});
// 先写入一个空字符串 让 fetch resolve
res.write("");
我发现必须 res.write(""); 才可以提前返回 header 否则是和 第一个 body chunk 一起返回的
是的,这是一个很好的观察!让我解释一下这个现象:
在 HTTP 协议中,服务器发送响应的顺序是:
- 状态行(Status Line)
- 响应头(Headers)
- 空行
- 响应体(Body)
当你使用 res.writeHead() 设置响应头时,数据并不会立即发送到客户端。Node.js 会将头信息缓存起来,直到:
- 第一次调用 res.write()
- 或者调用 res.end()
// 这样写,headers 会和第一块 body 数据一起发送
res.writeHead(200, {
"Content-Type": "application/json"
});
res.write(JSON.stringify({ some: "data" }));
// 这样写,headers 会立即发送,因为有一个空的 write
res.writeHead(200, {
"Content-Type": "application/json"
});
res.write(""); // 触发 headers 发送
res.write(JSON.stringify({ some: "data" }));
这种行为在实现流式响应时特别有用,比如:
// 示例:实现流式响应
res.writeHead(200, {
"Content-Type": "text/plain",
"Transfer-Encoding": "chunked"
});
res.write(""); // 立即发送 headers
// 后续可以分块发送数据
setInterval(() => {
res.write(`数据块 ${Date.now()}\n`);
}, 1000);