JavaScript中如何使用Fetch API发送请求?

来源:JS脚本作者:又改需求头衔:程序员
导读:本期聚焦于又改需求创作的《JavaScript中如何使用Fetch API发送请求?》,敬请观看详情。从XMLHttpRequest到Fetch API,浏览器发起HTTP请求的底层逻辑发生了明显变化。Fetch基于Promise设计,返回Response对象,配合async/await能让异步代码更接近同步写法。本文不重复官方文档的API列表,而是围绕GET、POST、文件上传、超时控制、错误处理、取消请求等实际场景展开,对比传统XHR的差异,并指出几个容易踩的坑:比如fetch只在网络故障时reject,对404状态码并不抛错;又比如取消请求需要AbortController配合signal。阅读完可以掌握如何在项目中稳定使用Fetch,以及何时仍需要退回XHR或axios。

Fetch API 是浏览器原生提供的用于发起 HTTP 请求的接口,它基于 Promise 设计,相比传统的 XMLHttpRequest,代码更简洁,也更容易和 async/await 配合使用。核心方法是 fetch(),它接收一个 URL 和一个可选的配置对象,返回一个 Promise,该 Promise 会解析为 Response 对象。借助 Response 对象上的 status、ok 属性以及 json()、text() 等方法,开发者可以方便地读取响应内容和状态信息。

JavaScript中如何使用Fetch API发送请求?

Fetch的基本语法与响应对象

调用 fetch() 的最简单形式是只传入一个 URL,此时默认发起 GET 请求。fetch() 返回的 Promise 在请求完成(无论成功或失败)后解析为 Response 对象,但需要注意一个关键点:只要网络层面没有发生错误,即使服务器返回 404 或 500 状态码,Promise 依然会正常 resolve,而不会 reject。这与很多人的直觉不同,也是实际开发中容易漏掉错误处理的根源。

下面是一个最基础的使用示例,展示如何获取数据并解析为 JSON:

fetch('/api/users')
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(error => console.error('请求失败:', error));

使用 async/await 重写上面的逻辑,代码结构会更加清晰。await fetch() 会直接得到 Response 对象,然后调用 response.json() 得到数据。但要注意,response.json() 本身也是一个异步操作,需要再次 await。如果响应不是合法的 JSON 格式,json() 方法会抛出一个 SyntaxError,需要在 catch 中处理。

Response 对象还提供了 text()、blob()、arrayBuffer()、formData() 等读取方法,分别对应文本、二进制文件、原始缓冲区和表单数据。每种方法都只能调用一次,因为响应体是流式的,读取后即被消费。如果需要同时保留原始文本和解析后的对象,可以先调用 text() 再手动 JSON.parse,但这样会多一次内存拷贝,在数据量较大时需要权衡。

发送GET请求与查询参数构造

GET 请求通常用于获取数据,参数一般拼接在 URL 后面。手动拼接字符串容易忘记 encodeURIComponent 导致特殊字符出错,例如用户输入了 & 或 = 符号,直接拼入 URL 会被误解析为参数分隔符。推荐使用 URLSearchParams 对象来构造查询字符串,它会自动处理编码问题。

下面的代码演示了如何构造带参数的 GET 请求:

const params = new URLSearchParams({
  keyword: '前端开发',
  page: 1,
  size: 20
});
const url = `/api/search?${params.toString()}`;
fetch(url)
  .then(response => response.json())
  .then(data => console.log(data));

如果 URL 本身已经包含部分查询参数,还可以通过 new URL() 解析后修改 searchParams 属性,这样能避免重复的问号或与号。另外,GET 请求默认不需要设置 Content-Type 请求头,因为 GET 请求没有请求体。如果需要携带认证信息,可以在 headers 中设置 Authorization 或者使用 credentials 选项控制 Cookie 的发送行为。

还有一个细节是缓存。fetch 默认遵循 HTTP 缓存机制,但如果想强制刷新,可以在配置对象中设置 cache: 'no-cache' 或 cache: 'reload'。对于实时性要求高的接口,设置合适的缓存策略可以减少不必要的网络请求。

发送POST请求与请求头配置

POST 请求用来提交数据给服务器,常见的数据格式有 JSON、表单编码和 FormData。对于 JSON 数据,需要手动设置 Content-Type 为 application/json,并使用 JSON.stringify 将对象转为字符串。如果不设置 Content-Type,服务器可能无法正确解析请求体。

fetch('/api/users', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    'Authorization': 'Bearer token123'
  },
  body: JSON.stringify({ name: '张三', age: 28 })
})
  .then(response => {
    if (!response.ok) {
      throw new Error(`HTTP 错误,状态码:${response.status}`);
    }
    return response.json();
  })
  .then(data => console.log('创建成功:', data))
  .catch(error => console.error('请求失败:', error));

上传文件时使用 FormData 对象,将文件或表单字段追加进去,然后直接作为 body 传入。此时不需要手动设置 Content-Type,浏览器会自动生成带有 boundary 的 multipart/form-data 请求头,手动设置反而会导致 boundary 丢失。

const formData = new FormData();
formData.append('avatar', fileInput.files[0]);
formData.append('username', '李四');

fetch('/api/upload', {
  method: 'POST',
  body: formData
})
  .then(response => response.json())
  .then(data => console.log('上传结果:', data));

跨域请求时,如果服务器没有正确配置 CORS 响应头,浏览器会阻止读取响应。对于需要携带 Cookie 的跨域请求,必须同时设置 credentials: 'include',并且服务器返回的 Access-Control-Allow-Origin 不能是通配符 *,必须指定具体域名。此外,一些自定义请求头会触发预检请求(OPTIONS),服务器需要正确处理 OPTIONS 方法才能完成后续请求。

错误处理与超时控制

前面提到,fetch 只有在网络故障(如 DNS 解析失败、连接被拒绝、跨域被浏览器拦截)时才会 reject,而对于 404、500 等 HTTP 错误状态,Promise 会正常 resolve。因此必须在 then 回调中检查 response.ok 或 response.status 来判断请求是否真正成功。常见的做法是封装一个判断函数,将非 2xx 状态码统一抛出异常。

下面展示一个带有完整错误处理的封装示例:

async function request(url, options = {}) {
  const response = await fetch(url, options);
  if (!response.ok) {
    const errorText = await response.text();
    throw new Error(`请求失败,状态码 ${response.status},信息:${errorText}`);
  }
  const contentType = response.headers.get('content-type');
  if (contentType && contentType.includes('application/json')) {
    return await response.json();
  }
  return await response.text();
}

Fetch 本身不支持 timeout 参数,不像 XHR 有 timeout 属性。要实现超时控制,需要结合 AbortController 和 setTimeout。当超时时间到达时调用 controller.abort(),fetch 会 reject 一个名为 AbortError 的异常。下面的代码演示了如何给请求加上 5 秒超时限制:

function fetchWithTimeout(url, options = {}, timeout = 5000) {
  const controller = new AbortController();
  const timer = setTimeout(() => controller.abort(), timeout);
  return fetch(url, { ...options, signal: controller.signal })
    .finally(() => clearTimeout(timer));
}

fetchWithTimeout('/api/slow-task', {}, 5000)
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(err => {
    if (err.name === 'AbortError') {
      console.error('请求超时被取消');
    } else {
      console.error('请求失败:', err);
    }
  });

需要注意的是,abort() 触发后,如果服务器已经收到请求,可能无法真正中止服务器端的处理,只能停止客户端等待。对于需要严格控制资源的场景,应该让服务器端支持幂等操作或通过取消令牌等方式配合。

取消请求与并发控制

AbortController 不仅可以用于超时,也可以用于用户主动取消请求,比如在搜索输入框中停止上一次未完成的请求、切换页面时中断正在进行的下载等。每个 fetch 请求都需要一个独立的 signal,否则多个请求会同时被取消。一个 controller 只能被 abort 一次,如果需要复用,需要重新创建新的 AbortController 实例。

let currentController = null;

async function search(keyword) {
  if (currentController) {
    currentController.abort();
  }
  currentController = new AbortController();
  const signal = currentController.signal;
  try {
    const response = await fetch(`/api/search?q=${encodeURIComponent(keyword)}`, { signal });
    const data = await response.json();
    renderResults(data);
  } catch (err) {
    if (err.name === 'AbortError') {
      console.log('上一次搜索已取消');
    } else {
      console.error('搜索失败:', err);
    }
  }
}

在并发请求场景中,Promise.all 会在任意一个 Promise reject 时立即失败,而 Promise.allSettled 会等待所有 Promise 完成,不论成功或失败。如果需要对多个请求统一设置取消,可以让它们共享同一个 signal,这样任意一个触发 abort 或外部调用 abort 都会取消全部请求。另外,对于大量并发请求,建议控制并发数量,避免浏览器连接数上限导致请求排队。

与XMLHttpRequest的对比及封装建议

相比 XMLHttpRequest,Fetch API 在代码简洁性、Promise 原生支持、Response 对象设计上都有明显优势。XHR 需要监听 onreadystatechange 事件,手动判断 readyState 和 status,代码较为冗余。而 Fetch 的错误处理虽然需要额外判断 response.ok,但整体逻辑更加线性。不过 XHR 也有 Fetch 不具备的能力,比如上传进度事件 onprogress、下载进度事件,以及 abort 后可以继续复用对象等。如果项目中需要展示上传进度条,目前 Fetch 还没有原生支持,可能需要借助 service worker 或流式读取,实现起来比较复杂。

下面是一个 XHR 的典型写法,用来和 Fetch 做对照:

const xhr = new XMLHttpRequest();
xhr.open('GET', '/api/users');
xhr.onreadystatechange = function() {
  if (xhr.readyState === 4) {
    if (xhr.status >= 200 && xhr.status < 300) {
      console.log(JSON.parse(xhr.responseText));
    } else {
      console.error('请求失败,状态码:' + xhr.status);
    }
  }
};
xhr.onerror = function() {
  console.error('网络错误');
};
xhr.send();

在实际项目中,直接使用原生 Fetch 可能会遇到重复代码较多的问题,建议封装一个统一的请求模块,集中处理 baseURL、认证头、超时、错误提示、重试逻辑等。如果项目已经使用 Vue、React 等框架,可以结合框架的拦截器机制。对于需要上传进度、取消令牌、请求重试等高级功能的场景,axios 等第三方库仍然是更省心的选择,它们内部基于 XHR 或 Fetch 实现,但提供了更完善的接口和拦截器体系。理解 Fetch 的底层机制,有助于在需要时自行实现轻量封装,减少对库的依赖。

Fetch APIJavaScript异步请求修改时间:2026-09-25 09:51:39

免责声明:已尽一切努力确保本网站所含信息的准确性。网站作品多为原创整理与精心创作,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们进行处理Email:chomcom@qq.com。
引用或转载本作品时,请注明当前出处:https://www.ipipp.com/html/0925/61660.html,基于非商业用途的前提下,欢迎转载或二创本作品。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。