news 2026/10/8 7:23:49

Modern JavaScript Tutorial 的 fetch() 网络请求完全指南:从基础语法到实战应用

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Modern JavaScript Tutorial 的 fetch() 网络请求完全指南:从基础语法到实战应用
  • 文档/教程
  • 前端

【免费下载链接】en.javascript.info

Modern JavaScript Tutorial

项目地址:https://gitcode.com/gh_mirrors/en/en.javascript.info
点击查看免费下载

fetch()是现代浏览器内置的网络请求 API,允许 JavaScript 在不刷新页面的情况下向服务器发送请求、加载新信息。本文以《Modern JavaScript Tutorial》仓库中 5-network/01-fetch/article.md 为骨架,结合仓库内的服务端演示代码与配套练习题,系统讲解 fetch 的两阶段响应模型、响应体读取方法、请求/响应头操作、POST 请求、二进制数据上传,以及并发拉取 GitHub 用户信息的实战模式。读完本文,你将掌握用fetch完成从简单 GET 到表单、JSON、图片上传的完整网络通信方案。

一、为什么需要 fetch:AJAX 与网络请求

JavaScript 可以随时向服务器发送网络请求并加载新信息,典型场景包括:

  • 提交订单;
  • 加载用户信息;
  • 从服务器接收最新更新;
  • ……等等。

所有这些操作都不需要重新加载页面。这类"从 JavaScript 发起网络请求"的做法有一个统称AJAX(AsynchronousJavaScriptAndXML)。虽然名称里带有 XML,但如今并不强制使用 XML——这个词来自早期技术时代。你或许已经听说过它。

发送网络请求并获取服务器信息的方式有很多种,而fetch()是其中现代且功能全面的方法。它不被旧浏览器支持(但可以通过 polyfill 补充),在主流现代浏览器中支持度非常好。

二、fetch 基础语法与两阶段响应模型

fetch()的基本语法非常简洁:

let promise = fetch(url, [options])
  • url—— 要访问的 URL;
  • options—— 可选参数:method、headers 等。

如果不传options,则默认发起一个简单的 GET 请求,下载url的内容。浏览器会立即发起请求,并返回一个 Promise,调用方代码使用它来获取结果。

获取响应通常是一个两阶段的过程:

第一阶段:fetch返回的 Promise 在服务器响应头到达时立即 resolve,得到一个内置Response类的对象。在这一阶段,你可以检查 HTTP 状态码(判断请求是否成功)、查看响应头,但还拿不到响应体。

如果fetch无法发起 HTTP 请求(例如网络问题、站点不存在),Promise 会 reject。注意:异常的 HTTP 状态码(如 404、500)并不会导致 Promise 报错,它们只是普通的成功响应。

在响应对象上可以查看 HTTP 状态:

  • status—— HTTP 状态码,例如 200;
  • ok—— 布尔值,HTTP 状态码为 200-299 时为true。

典型写法:

let response = await fetch(url); if (response.ok) { // HTTP-status 为 200-299 // 获取响应体(方法见下文) let json = await response.json(); } else { alert("HTTP-Error: " + response.status); }

第二阶段:读取响应体需要额外的方法调用。

Response提供了多个基于 Promise 的方法,以不同格式读取响应体:

  • response.text()—— 将响应读取为文本;
  • response.json()—— 将响应解析为 JSON;
  • response.formData()—— 将响应作为FormData对象返回(详见下一章 FormData);
  • response.blob()—— 将响应作为 Blob 返回(带类型的二进制数据);
  • response.arrayBuffer()—— 将响应作为 ArrayBuffer 返回(二进制数据的底层表示);
  • 另外,response.body是一个ReadableStream对象,允许你逐块读取响应体,后文会给出示例。

实战:从 GitHub API 拉取 JSON

例如,获取 GitHub 上最新提交的 JSON 对象:

let url = 'https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits'; let response = await fetch(url); let commits = await response.json(); // 读取响应体并解析为 JSON alert(commits[0].author.login);

同样的逻辑,不用await,使用纯 Promise 语法:

fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits') .then(response => response.json()) .then(commits => alert(commits[0].author.login));

要获取响应文本,用await response.text()代替.json():

let response = await fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits'); let text = await response.text(); // 将响应体读取为文本 alert(text.slice(0, 80) + '...');

实战:以 Blob 方式下载并展示二进制内容

作为二进制格式读取的展示案例,仓库文章演示了下载 "fetch" 规范,关于Blob的操作详见 Blob 章节):

let response = await fetch('/article/fetch/logo-fetch.svg'); let blob = await response.blob(); // 作为 Blob 对象下载 // 为它创建 <img> let img = document.createElement('img'); img.style = 'position:fixed;top:10px;left:10px;width:100px'; document.body.append(img); // 展示它 img.src = URL.createObjectURL(blob); setTimeout(() => { // 三秒后隐藏 img.remove(); URL.revokeObjectURL(img.src); }, 3000);

重要限制:只能选择一种响应体读取方法

注意:只能选择一种读取响应体的方法。

如果已经通过response.text()获取了响应,那么response.json()将无法工作,因为响应体内容已被消费。

let text = await response.text(); // 响应体已被消费 let parsed = await response.json(); // 失败(已消费)

三、Response headers:读取响应头

响应头位于response.headers,这是一个类似 Map 的对象。它不完全是 Map,但提供了类似的方法,可以按名称获取单个头或遍历所有头:

let response = await fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits'); // 获取单个响应头 alert(response.headers.get('Content-Type')); // application/json; charset=utf-8 // 遍历所有响应头 for (let [key, value] of response.headers) { alert(`${key} = ${value}`); }

四、Request headers:设置请求头与禁止头列表

要设置请求头,可以在fetch中使用headers选项,传入一个包含出站请求头的对象:

let response = fetch(protectedUrl, { headers: { Authentication: 'secret' } });

但存在一份**禁止设置的 HTTP 头(forbidden HTTP headers)**列表,浏览器不允许你设置它们:

  • Accept-Charset、Accept-Encoding
  • Access-Control-Request-Headers
  • Access-Control-Request-Method
  • Connection
  • Content-Length
  • Cookie、Cookie2
  • Date
  • DNT
  • Expect
  • Host
  • Keep-Alive
  • Origin
  • Referer
  • TE
  • Trailer
  • Transfer-Encoding
  • Upgrade
  • Via
  • Proxy-*
  • Sec-*

这些头由浏览器独占控制,以确保 HTTP 的正确性与安全性。

五、POST 请求:提交 JSON 数据

要发起POST请求或其他方法,需要用到fetch的选项:

  • method—— HTTP 方法,例如POST;
  • body—— 请求体,可以是以下之一:
    • 字符串(例如 JSON 编码的文本);
    • FormData对象,以multipart/form-data提交数据;
    • Blob/BufferSource,用于发送二进制数据;
    • URLSearchParams,以x-www-form-urlencoded编码提交数据,较少使用。

绝大多数情况下使用 JSON 格式。例如,下面的代码将user对象作为 JSON 提交:

let user = { name: 'John', surname: 'Smith' }; let response = await fetch('/article/fetch/post/user', { method: 'POST', headers: { 'Content-Type': 'application/json;charset=utf-8' }, body: JSON.stringify(user) }); let result = await response.json(); alert(result.message);

请注意:如果请求body是字符串,Content-Type头默认会被设置为text/plain;charset=UTF-8。但由于我们要发送 JSON,所以通过headers选项显式改为application/json——这才是 JSON 编码数据正确的Content-Type。

仓库中的配套服务端实现

文章中的/article/fetch/post/user路由对应仓库里的演示服务 5-network/01-fetch/post.view/server.js。它是一个基于 Koa 的小型服务器,源码如下:

const Koa = require('koa'); const app = new Koa(); const bodyParser = require('koa-bodyparser'); const getRawBody = require('raw-body') const Router = require('koa-router'); let router = new Router(); router.post('/user', async (ctx) => { ctx.body = { message: "User saved." }; }); router.post('/image', async (ctx) => { let body = await getRawBody(ctx.req, { limit: '1mb' }); ctx.body = { message: `Image saved, size:${body.length}.` }; }); app .use(bodyParser()) .use(router.routes()) .use(router.allowedMethods()); if (!module.parent) { http.createServer(app.callback()).listen(8080); } else { exports.accept = app.callback(); }

从源码可以看到:POST /user返回{ message: "User saved." },POST /image通过raw-body读取原始请求体(限制为 1MB),返回图片大小。这印证了文章示例中response.json()读到的result.message的来源;同时limit: '1mb'也提示了上传二进制数据的体量边界。该服务器默认监听 8080 端口,同时导出了accept回调供测试环境挂载。

六、发送二进制数据:用 Blob 上传图片

fetch也可以使用Blob或BufferSource对象提交二进制数据。

文章中的示例:页面上有一个<canvas>,可以通过移动鼠标在上面绘图,点击 "Submit" 按钮把图片发送到服务器:

<body style="margin:0"> <canvas id="canvasElem" width="100" height="80" style="border:1px solid"></canvas> <input type="button" value="Submit" onclick="submit()"> <script> canvasElem.onmousemove = function(e) { let ctx = canvasElem.getContext('2d'); ctx.lineTo(e.clientX, e.clientY); ctx.stroke(); }; async function submit() { let blob = await new Promise(resolve => canvasElem.toBlob(resolve, 'image/png')); let response = await fetch('/article/fetch/post/image', { method: 'POST', body: blob }); // 服务器返回确认信息和图片大小 let result = await response.json(); alert(result.message); } </script> </body>

请注意:这里我们没有手动设置Content-Type头,因为Blob对象自带类型(这里由toBlob生成image/png)。对于Blob对象,这个类型会自动成为Content-Type的值。配合上一节的服务端代码,POST /image正是用getRawBody读取这个 Blob 的原始字节并返回其大小。

不使用async/await时,submit()函数可以改写成纯 Promise 形式:

function submit() { canvasElem.toBlob(function(blob) { fetch('/article/fetch/post/image', { method: 'POST', body: blob }) .then(response => response.json()) .then(result => alert(JSON.stringify(result, null, 2))) }, 'image/png'); }

七、实战练习:并发拉取 GitHub 用户

仓库在 5-network/01-fetch/01-fetch-users/task.md 中提供了一个综合练习:创建一个异步函数getUsers(names),接收 GitHub 登录名数组,从 GitHub 拉取用户信息并返回用户数组。给定USERNAME的 GitHub 用户信息 URL 为https://api.github.com/users/USERNAME。关键要求:

  1. 每个用户发起一次fetch请求;
  2. 请求之间互不等待,让数据尽快到达;
  3. 如果某个请求失败或用户不存在,在结果数组中返回null。

仓库中的参考解法见 5-network/01-fetch/01-fetch-users/solution.md,对应源码在 5-network/01-fetch/01-fetch-users/_js.view/solution.js:

async function getUsers(names) { let jobs = []; for(let name of names) { let job = fetch(`https://api.github.com/users/${name}`).then( successResponse => { if (successResponse.status != 200) { return null; } else { return successResponse.json(); } }, failResponse => { return null; } ); jobs.push(job); } let results = await Promise.all(jobs); return results; }

这个解法的精妙之处在于:.then直接挂在fetch上,这样一旦拿到响应就立即开始读取.json(),不会等待其他请求。如果改成await Promise.all(names.map(name => fetch(...)))再对结果调用.json(),就会等待所有 fetch 响应完成才逐一解析。把.json()直接接在每个fetch后面,可以保证每个请求独立地、尽早地开始按 JSON 读取数据。

配套测试 5-network/01-fetch/01-fetch-users/_js.view/test.js 验证了该行为:

describe("getUsers", function() { it("gets users from GitHub", async function() { let users = await getUsers(['iliakan', 'remy', 'no.such.users']); assert.equal(users[0].login, 'iliakan'); assert.equal(users[1].login, 'remy'); assert.equal(users[2], null); }); });

测试用例同时覆盖了正常用户(iliakan、remy)与不存在的用户(no.such.users应返回null)。这也是"即使主要使用async/await,底层 Promise API 依然有用"的典型示例。

八、总结

一个典型的 fetch 请求由两次await调用组成:

let response = await fetch(url, options); // 响应头到达即 resolve let result = await response.json(); // 读取 JSON 响应体

或者不用await:

fetch(url, options) .then(response => response.json()) .then(result => /* 处理结果 */)

响应属性:

  • response.status—— 响应的 HTTP 状态码;
  • response.ok—— 状态码为 200-299 时为true;
  • response.headers—— 类似 Map 的 HTTP 响应头对象。

读取响应体的方法:

  • response.text()—— 将响应作为文本返回;
  • response.json()—— 将响应解析为 JSON 对象;
  • response.formData()—— 将响应作为FormData对象返回(multipart/form-data编码,见下一章);
  • response.blob()—— 将响应作为 Blob 返回(带类型的二进制数据);
  • response.arrayBuffer()—— 将响应作为 ArrayBuffer 返回(底层二进制数据)。

目前介绍的 fetch 选项:

  • method—— HTTP 方法;
  • headers—— 包含请求头的对象(并非所有头都允许设置);
  • body—— 要发送的数据(请求体),可以是string、FormData、BufferSource、Blob或UrlSearchParams对象。

在后续章节中,本文所属的 网络请求目录 还会介绍fetch的更多选项与使用场景,例如 FormData 表单提交、下载进度、中断请求、跨源请求 等,本文介绍的两阶段模型、headers/body/method选项与 Promise 并发技巧是理解它们的基础。

  • 文档/教程
  • 前端

【免费下载链接】en.javascript.info

Modern JavaScript Tutorial

项目地址:https://gitcode.com/gh_mirrors/en/en.javascript.info
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/8 7:21:05

美国TRO禁令是什么?亚马逊卖家必须了解的风险

先说结论收到TRO&#xff0c;并不等于法院已经最终认定你侵权&#xff1b;但如果完全不处理&#xff0c;它又可能持续影响后续案件走向。做美国站的跨境卖家&#xff0c;可能听过这样一句话&#xff1a;“店铺被TRO了。”但很多卖家真正遇到以后才发现&#xff0c;自己其实并不…

作者头像 李华
网站建设 2026/10/8 7:20:20

FeitCSI v2.0.0 修复 dat 文件 timestamp=0问题

0. 找到 FeitCSI 源码目录方式 1&#xff1a;搜索文件定位find ~ -name "FeitCSI" -type d执行后会输出类似&#xff1a;/home/h/FeitCSI-iwlwifi/FeitCSI这就是源码根目录&#xff0c;复制这个路径。进入源码目录&#xff08;把下面路径替换成你搜索出来的真实路径&…

作者头像 李华
网站建设 2026/10/8 7:20:08

279模式深度解析:[279模式的最新发展趋势2025]

279模式深度解析&#xff1a;279模式的最新发展趋势2025摘要/引言本白皮书旨在深入探讨“279模式”的核心理念、操作机制及其在当前商业环境中的应用价值。随着市场竞争的日益激烈和消费者需求的多样化&#xff0c;企业正积极寻求创新的商业模式以实现可持续增长。本文将重点解…

作者头像 李华