JavaScriptconst 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 等信息,决定后续如何处理
JavaScripttry { 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 分离外,还搞了流式获取
Note JavaScriptres.writeHead(200, { "Content-Type": "application/json", }); // 先写入一个空字符串 让 fetch resolve res.write("");
我发现必须 res.write(""); 才可以提前返回 header 否则是和 第一个 body chunk 一起返回的
是的,这是一个很好的观察!让我解释一下这个现象:
在 HTTP 协议中,服务器发送响应的顺序是:
当你使用 res.writeHead() 设置响应头时,数据并不会立即发送到客户端。Node.js 会将头信息缓存起来,直到:
JavaScript// 这样写,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" }));
这种行为在实现流式响应时特别有用,比如:
JavaScript// 示例:实现流式响应 res.writeHead(200, { "Content-Type": "text/plain", "Transfer-Encoding": "chunked" }); res.write(""); // 立即发送 headers // 后续可以分块发送数据 setInterval(() => { res.write(`数据块 ${Date.now()}\n`); }, 1000);