F10第 1 / 1 课时未开始内容版本 1.0.0 · 核验于 2026-10-26

HTTP、Fetch 与取消请求

HTTP、Fetch 与取消请求 · 阅读后完成下方实验与检查题,再用「标记完成」记录进度。

学习目标

这一课用 fetch(浏览器内置的发请求接口)请求一个分页接口,把「成功、失败、脏数据、取消」四条路都走通,并对照 Network 面板理解请求头、状态码和取消在底层的样子。读完你应该能写出一个不白屏、不卡死、不串台的请求函数。

问题场景

课程列表翻到第 2 页要 2 秒,用户等不及点了第 3 页,又点了第 1 页。三个请求同时在飞,最后完成的却是第 2 页的——界面显示了第 2 页的数据,选中的却是第 1 页。这类「慢请求覆盖新请求」的串台,加上没检查 res.ok 导致的「把错误页当数据渲染」,是 fetch 代码最常见的两类事故。

核心解释

fetch 有两个和直觉不同的点,先立规矩:

  1. fetch 只在网络层失败时拒绝:404、500 也算「成功拿到了响应」,Promise 照样 resolve。所以拿到响应的第一件事是检查 res.ok(状态码 200–299 时为 true)。
  2. res.json() 也可能抛错:响应体不是 JSON(比如错误页的 HTML)时解析会抛异常,要放进同一个 try 里。
async function loadCourses(page, signal) {
  const res = await fetch(`/api/courses?page=${page}`, { signal });
  if (!res.ok) {
    throw new Error(`请求失败:HTTP ${res.status}`); // 把状态码带给 catch
  }
  const data = await res.json(); // 可能抛错:返回的不是 JSON
  if (!Array.isArray(data.items)) {
    throw new Error('响应格式不符合预期'); // 运行时校验,别信接口
  }
  return data;
}

取消请求用 AbortController(浏览器提供的取消信号控制器):把它的 signal 传给 fetch,调用 abort() 时请求被中止,fetch 抛出特定错误:

const controller = new AbortController();
loadCourses(2, controller.signal).catch((err) => {
  if (err.name === 'AbortError') return; // 主动取消,不算失败
  showError(err.message);
});
controller.abort(); // 比如用户又点了别的页

示例拆解

修「翻页串台」的套路:每次新请求前,取消上一个还没完成的:

let controller = null;

async function goToPage(page) {
  controller?.abort(); // 上一页还没回来?取消它
  controller = new AbortController();
  try {
    const data = await loadCourses(page, controller.signal);
    render(data); // 只有最后选中的页才可能走到这里
  } catch (err) {
    if (err.name !== 'AbortError') showError(err.message);
  }
}
  • 关键点:被 abort 的请求会拒绝并带上 AbortError,渲染代码不会执行——第 2 页的慢响应再也覆盖不了第 1 页。
  • 到 Network 面板验证:被取消的请求 Status 列显示 canceled(红色),说明浏览器真的中止了它,而不是前端假装没看见。
  • 请求头也在 Network 里看:Request Headers 里的 Accept 是浏览器自动加的;以后加登录态,就是在那里放 Authorization。

动手任务

本课没有在线实验,用真实浏览器完成(接口用公开测试服务 https://jsonplaceholder.typicode.com/posts?_page=1&_limit=5):

  1. 在控制台用 fetch 请求上面的地址,打印 res.status、res.ok 和 res.headers.get('content-type'),再到 Network 面板找到这条请求对照。
  2. 把地址改成不存在的 /postsXYZ,确认 fetch 不抛错而是返回 404;然后给你的请求函数加上 res.ok 检查。
  3. 快速连续改变 _page 参数发三次请求,前两次都调用 abort 取消,观察 Network 面板里的 canceled 状态,确认只有最后一次的结果被渲染。

常见错误

  • 不检查 res.ok:404 的响应体也可能被当数据渲染,报错信息完全看不懂;请求函数第一行就是 if (!res.ok) throw。
  • catch 里不区分 AbortError:把用户主动取消当失败弹红字提示,用户一脸懵。
  • 用防抖治串台:防抖只能少发请求,慢请求照样可能覆盖新请求;正确做法是 abort 旧请求。
  • 信接口不信校验:接口哪天把 items 改成对象,页面直接白屏;边界处用 Array.isArray 这类运行时检查兜底。

检查与复盘

完成下方检查题确认理解。自查清单:

  • 能说清 fetch 什么时候拒绝、什么时候 resolve,以及 res.ok 的作用
  • 写请求函数有固定骨架:检查状态 → 校验形状 → 带上下文抛错
  • 能在 Network 面板找到 canceled 请求并解释 abort 的效果

参考资料

检查题共 2 题,提交后立即显示解析
答案由服务端判定;答错的题会自动进入复习计划。
Q1读代码中等

阅读代码(浏览器环境,现代浏览器均支持 AbortController)。控制台输出什么?第一次请求还会影响后续界面吗?

const controller = new AbortController();

async function search(keyword, signal) {
  const res = await fetch('/api/search?q=' + keyword, { signal });
  const data = await res.json();
  console.log('结果', keyword);
  return data;
}

search('a', controller.signal).catch((e) => console.log('捕获', e.name));
controller.abort();
Q2单选简单

fetch('/api/user/999') 的响应状态码是 404。下列说法正确的是?

读完这一课了?

标记完成后,工作台会安排接下来的学习。

Command Palette

Search for a command to run...