JavaScript 异步请求 / Axios
🎯 目标:2-3小时掌握 Axios 核心用法,达到实战水平
📑 目录
1. 核心概念
1.1 什么是 Axios?
- 定义:基于 Promise 的 HTTP 客户端,可在浏览器和 Node.js 中使用。比原生 fetch 更易用,自动转换 JSON。
 - 为什么用它:统一的错误处理、请求/响应拦截器、取消请求、超时控制、防 XSRF 等开箱即用的功能。
 
1.2 Promise 基础(前置知识)
- Promise 三态:
pending(进行中)→fulfilled(成功)或rejected(失败)。通过.then()处理成功,.catch()处理失败。 - async/await:Promise 的语法糖,让异步代码看起来像同步代码。
await只能在async函数内使用。 
1.3 HTTP 请求核心要素
- 方法:
GET(查询)、POST(创建)、PUT(更新)、DELETE(删除)、PATCH(部分更新)。 - 状态码:
2xx(成功)、4xx(客户端错误,如 404)、5xx(服务器错误,如 500)。 
2. 必须掌握的 API
2.1 基础请求方法
axios.get(url, config)        // GET 请求,参数通过 config.params 传递
axios.post(url, data, config) // POST 请求,data 是请求体
axios.put(url, data, config)  // PUT 全量更新
axios.delete(url, config)     // DELETE 删除
2.2 请求配置对象(config)
{
  url: '/api/users',           // 请求路径
  method: 'get',               // 默认 GET
  params: { id: 1 },           // URL 查询参数 (?id=1)
  data: { name: 'John' },      // 请求体(POST/PUT/PATCH)
  headers: { 'X-Token': 'abc' }, // 自定义请求头
  timeout: 5000,               // 超时时间(毫秒),超时自动取消请求
  responseType: 'json'         // 响应数据类型(默认 json)
}
2.3 响应对象结构
{
  data: {},           // 服务器返回的数据
  status: 200,        // HTTP 状态码
  statusText: 'OK',   // 状态文本
  headers: {},        // 响应头
  config: {},         // 请求配置
  request: {}         // 原始请求对象
}
2.4 创建实例
const api = axios.create({
  baseURL: 'https://api.example.com', // 基础 URL,所有请求自动拼接
  timeout: 10000,                     // 全局超时
  headers: { 'Content-Type': 'application/json' }
});
2.5 拦截器(Interceptors)
// 请求拦截器:发送前修改配置(如添加 token)
api.interceptors.request.use(
  config => { config.headers.Authorization = 'Bearer token'; return config; },
  error => Promise.reject(error)
);
// 响应拦截器:统一处理响应或错误(如 token 过期跳转登录)
api.interceptors.response.use(
  response => response.data, // 直接返回 data,简化调用
  error => { if(error.response?.status === 401) { /*跳转登录*/ } return Promise.reject(error); }
);
3. 典型应用场景与设计取舍
| 场景 | 方案 | 取舍理由 | 
|---|---|---|
| 简单页面(几个请求) | 直接用 axios.get/post | 
无需封装,代码直观 | 
| 中大型项目 | 创建实例 + 拦截器 + API 模块化 | 统一配置(baseURL/token),便于维护 | 
| 并发请求(同时获取多个资源) | Promise.all([req1, req2]) | 
全部成功才处理,一个失败全失败 | 
| 竞态问题(快速切换导致旧请求覆盖新请求) | 用 AbortController 取消旧请求 | 
确保只处理最新请求的结果 | 
| 轮询(定时刷新数据) | setInterval + axios | 
简单场景可用,复杂的用 WebSocket | 
| 文件上传 | FormData + axios.post | 
支持进度监听(onUploadProgress) | 
设计建议:
- 统一错误处理放在响应拦截器,避免每个请求都写 
try-catch。 - 将 API 按模块分文件(如 
userApi.js、productApi.js),导出函数而非裸请求。 
4. 最小可运行示例
4.1 环境准备
# 方式1:CDN(直接在 HTML 中使用)
# 在 <head> 中添加:
# <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
# 方式2:npm 安装(推荐,用于项目)
npm install axios
4.2 完整示例(HTML + JavaScript)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>Axios 实战示例</title>
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
</head>
<body>
  <h1>用户管理系统</h1>
  <!-- 获取用户列表 -->
  <button onclick="getUsers()">获取用户列表</button>
  <ul id="userList"></ul>
  <!-- 创建新用户 -->
  <h3>创建用户</h3>
  <input id="userName" placeholder="输入用户名">
  <button onclick="createUser()">创建</button>
  <script>
    // ==================== 1. 创建 Axios 实例 ====================
    const api = axios.create({
      baseURL: 'https://jsonplaceholder.typicode.com', // 免费测试 API
      timeout: 5000,
      headers: {
        'Content-Type': 'application/json'
      }
    });
    // ==================== 2. 添加请求拦截器 ====================
    api.interceptors.request.use(
      config => {
        console.log('📤 发送请求:', config.method.toUpperCase(), config.url);
        // 实际项目中在这里添加 token
        // config.headers.Authorization = 'Bearer ' + localStorage.getItem('token');
        return config;
      },
      error => {
        console.error('❌ 请求错误:', error);
        return Promise.reject(error);
      }
    );
    // ==================== 3. 添加响应拦截器 ====================
    api.interceptors.response.use(
      response => {
        console.log('✅ 响应成功:', response.status, response.data);
        return response.data; // 直接返回数据部分,简化后续调用
      },
      error => {
        // 统一错误处理
        if (error.response) {
          // 服务器返回了错误状态码
          console.error(`❌ 服务器错误 ${error.response.status}:`, error.response.data);
          alert(`请求失败: ${error.response.status} - ${error.response.statusText}`);
        } else if (error.request) {
          // 请求已发送但没收到响应(网络问题)
          console.error('❌ 网络错误:', error.request);
          alert('网络连接失败,请检查网络');
        } else {
          // 请求配置出错
          console.error('❌ 请求配置错误:', error.message);
        }
        return Promise.reject(error);
      }
    );
    // ==================== 4. 封装 API 函数 ====================
    // GET 请求:获取用户列表
    async function getUsers() {
      try {
        // 方式1:async/await(推荐,代码更清晰)
        const users = await api.get('/users', {
          params: { _limit: 5 } // 限制返回5条数据
        });
        // 渲染到页面
        const userList = document.getElementById('userList');
        userList.innerHTML = users.map(user => 
          `<li>${user.id}. ${user.name} (${user.email})</li>`
        ).join('');
      } catch (error) {
        console.error('获取用户失败:', error);
        // 错误已在拦截器中处理,这里可以做额外的业务逻辑
      }
    }
    // POST 请求:创建用户
    async function createUser() {
      const userName = document.getElementById('userName').value;
      if (!userName) {
        alert('请输入用户名');
        return;
      }
      try {
        const newUser = await api.post('/users', {
          name: userName,
          email: `${userName}@example.com`,
          username: userName
        });
        alert(`✅ 创建成功! 用户ID: ${newUser.id}`);
        document.getElementById('userName').value = ''; // 清空输入框
      } catch (error) {
        console.error('创建用户失败:', error);
      }
    }
    // ==================== 5. 其他常用示例 ====================
    // 并发请求
    async function getConcurrentData() {
      try {
        const [users, posts] = await Promise.all([
          api.get('/users?_limit=3'),
          api.get('/posts?_limit=3')
        ]);
        console.log('用户:', users);
        console.log('文章:', posts);
      } catch (error) {
        console.error('并发请求失败:', error);
      }
    }
    // 取消请求(防止重复点击)
    let cancelTokenSource = null;
    async function searchWithCancel(keyword) {
      // 取消上一次未完成的请求
      if (cancelTokenSource) {
        cancelTokenSource.cancel('新搜索开始,取消旧请求');
      }
      cancelTokenSource = axios.CancelToken.source();
      try {
        const results = await api.get('/users', {
          params: { q: keyword },
          cancelToken: cancelTokenSource.token
        });
        console.log('搜索结果:', results);
      } catch (error) {
        if (axios.isCancel(error)) {
          console.log('请求已取消:', error.message);
        } else {
          console.error('搜索失败:', error);
        }
      }
    }
    // ==================== 6. 演示:页面加载时自动获取数据 ====================
    window.onload = () => {
      console.log('🚀 页面加载完成,可点击按钮测试');
      // 自动加载用户列表
      // getUsers();
    };
  </script>
</body>
</html>
4.3 运行步骤
- 保存上述代码为 
axios-demo.html - 双击打开该文件(或用 Live Server 启动)
 - 点击"获取用户列表"按钮,查看控制台日志和页面渲染
 - 输入用户名,点击"创建"按钮测试 POST 请求
 - 打开浏览器开发者工具(F12)→ Network 标签,观察请求详情
 
5. 常见坑与调试技巧
5.1 常见错误
| 错误现象 | 原因 | 解决方案 | 
|---|---|---|
| CORS 跨域错误 | 浏览器安全策略,前端不同源访问后端 | 后端配置 CORS 头(Access-Control-Allow-Origin),或用代理 | 
| 401 Unauthorized | token 过期或未传递 | 在请求拦截器中添加 Authorization 头 | 
| 超时错误 | 网络慢或 timeout 设置太短 | 增加 timeout 值,或检查网络 | 
| 数据获取失败但不报错 | 忘记 return 或 await | 
确保异步函数用 await,箭头函数记得 return | 
| Cannot read property 'data' of undefined | 直接访问 response.data 但响应失败 | 
用 ?. 可选链:response?.data,或在拦截器统一处理 | 
5.2 调试技巧
- 
查看完整请求/响应
axios.get('/api/users').then(res => { console.log('完整响应对象:', res); console.log('请求配置:', res.config); console.log('响应头:', res.headers); }); - 
使用浏览器 Network 面板
- 查看请求 URL、方法、状态码
 - 检查 Request Headers(是否带 token)
 - 查看 Response 原始数据(是否符合预期)
 
 - 
拦截器打日志
api.interceptors.request.use(config => { console.log('📍 请求配置:', JSON.stringify(config, null, 2)); return config; }); - 
错误分类处理
catch(error => { if (error.response) { // 服务器返回错误(4xx/5xx) console.log('状态码:', error.response.status); console.log('错误数据:', error.response.data); } else if (error.request) { // 请求发出但无响应(网络问题) console.log('无响应,检查网络'); } else { // 请求配置错误 console.log('配置错误:', error.message); } }) 
6. 分层练习清单
🌱 入门级(必做)
- 
基础 CRUD 操作
- 用 JSONPlaceholder API 完成:获取文章列表(GET)、创建文章(POST)、更新文章(PUT)
 - 要求:用 async/await,在页面上显示结果
 
 - 
配置超时与错误处理
- 设置 3 秒超时,故意请求一个慢接口(或不存在的 URL)
 - 在 catch 中区分超时错误和 404 错误,弹出不同提示
 
 - 
URL 参数传递
- 实现搜索功能:输入关键词,用 
params传递给/users?name=xxx - 打印最终请求的完整 URL
 
 - 实现搜索功能:输入关键词,用 
 
🔨 巩固级(强化理解)
- 
创建 Axios 实例
- 创建一个实例,设置 
baseURL、默认 headers - 封装 3 个 API 函数:
getUserList()、getUserById(id)、deleteUser(id) 
 - 创建一个实例,设置 
 - 
请求/响应拦截器
- 请求拦截器:自动给所有请求添加时间戳参数(
_t=Date.now()) - 响应拦截器:如果 
response.data.code !== 200,自动 reject 并显示错误信息 
 - 请求拦截器:自动给所有请求添加时间戳参数(
 - 
并发请求处理
- 同时请求用户列表和文章列表,全部成功后合并渲染到页面
 - 用 
Promise.all()实现,处理其中一个失败的情况 
 
🚀 进阶级(实战场景)
- 
请求取消与防抖
- 实现搜索框:输入时实时搜索,快速输入时取消旧请求,只保留最新请求
 - 结合 
AbortController或CancelToken 
 - 
文件上传与进度
- 用 
<input type="file">选择图片,通过FormData上传 - 监听 
onUploadProgress,显示上传百分比(用进度条或文本) - 测试 API:可用 
https://httpbin.org/post 
 - 用 
 - 
Token 刷新机制
- 模拟场景:token 有效期 5 秒,过期后自动刷新 token 并重试原请求
 - 响应拦截器检测 401,调用刷新接口,更新 token 后重发请求
 
 
7. 自测清单与闭卷题
📋 自测清单(每项打✅表示掌握)
- [ ] 能解释 Promise、async/await 的作用和区别
 - [ ] 会用 
axios.get/post发送基本请求 - [ ] 理解 
params(查询参数)和data(请求体)的区别 - [ ] 知道如何配置 
timeout、headers、baseURL - [ ] 能读懂响应对象结构,知道从哪里取数据(
response.data) - [ ] 会创建 Axios 实例,理解实例的好处
 - [ ] 能写请求拦截器(如添加 token)
 - [ ] 能写响应拦截器(如统一错误处理)
 - [ ] 会用 
Promise.all()处理并发请求 - [ ] 知道如何取消请求(
CancelToken或AbortController) - [ ] 会在浏览器 Network 面板调试请求
 - [ ] 能区分三类错误:
error.response、error.request、配置错误 
📝 闭卷题(30分钟内完成)
第1题:代码分析(10分)
以下代码有什么问题?如何修复?
axios.get('https://api.example.com/users')
  .then(response => {
    console.log(response);
    return response.data.map(user => user.name);
  });
const userNames = getUserNames(); // 想获取用户名数组
console.log(userNames); // 期望打印 ['Alice', 'Bob']
标准答案:
问题:
1. axios.get 返回 Promise,但没有 return,导致 getUserNames 返回 undefined
2. 即使 return 了,外部直接赋值也是 undefined,因为 Promise 是异步的
修复方案:
// 方式1:async/await
async function getUserNames() {
  const response = await axios.get('https://api.example.com/users');
  return response.data.map(user => user.name);
}
// 使用时也要 await
const userNames = await getUserNames();
// 方式2:返回 Promise
function getUserNames() {
  return axios.get('https://api.example.com/users')
    .then(response => response.data.map(user => user.name));
}
getUserNames().then(names => console.log(names));
第2题:实战应用(15分)
实现一个用户登录功能,要求:
- 调用 
POST /api/login,传递{ username, password } - 登录成功后,将返回的 
token存到localStorage - 创建一个 Axios 实例,自动给所有请求添加 token(从 localStorage 读取)
 - 如果响应状态码是 401,清除 token 并跳转到登录页
 
写出完整代码(可以用伪代码表示跳转逻辑)。
标准答案:
// 1. 创建 Axios 实例
const api = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 5000
});
// 2. 请求拦截器:自动添加 token
api.interceptors.request.use(config => {
  const token = localStorage.getItem('token');
  if (token) {
    config.headers.Authorization = `Bearer ${token}`;
  }
  return config;
});
// 3. 响应拦截器:处理 401
api.interceptors.response.use(
  response => response.data,
  error => {
    if (error.response?.status === 401) {
      localStorage.removeItem('token');
      window.location.href = '/login'; // 或用路由跳转
    }
    return Promise.reject(error);
  }
);
// 4. 登录函数
async function login(username, password) {
  try {
    const response = await axios.post('https://api.example.com/api/login', {
      username,
      password
    });
    // 保存 token
    localStorage.setItem('token', response.data.token);
    console.log('登录成功');
    return true;
  } catch (error) {
    console.error('登录失败:', error);
    return false;
  }
}
// 5. 使用示例
login('admin', '123456').then(success => {
  if (success) {
    // 之后用 api 发送的请求都会自动带 token
    api.get('/user/profile').then(data => console.log(data));
  }
});
第3题:场景题(5分)
你在开发搜索功能,用户在输入框快速输入时,会连续发送多个请求。如果第1个请求很慢,第5个请求很快返回,就会导致旧数据覆盖新数据。
这种问题叫什么?有哪两种解决方案?(简要说明思路即可)
标准答案:
问题名称:竞态条件(Race Condition)
解决方案:
1. 取消旧请求:
   - 每次发新请求前,用 AbortController 或 CancelToken 取消上一个未完成的请求
   - 确保只有最新的请求会返回结果
2. 标记请求序号:
   - 给每个请求分配一个递增的 ID
   - 响应返回时,只处理 ID 最大的那个请求的数据
   - 需要维护一个全局变量记录最新的请求 ID
推荐方案1,代码更简洁,浏览器原生支持。
📚 补充资源
- 官方文档: https://axios-http.com/
 - 中文文档: https://www.axios-http.cn/
 - 测试 API: JSONPlaceholder (免费 REST API)
 - 进阶阅读: Promise/A+ 规范、HTTP 缓存策略、RESTful API 设计规范
 
✅ 学习检查点
完成以下检查,确认你已掌握:
- ✅ 能独立用 Axios 完成 GET/POST/PUT/DELETE 请求
 - ✅ 会创建实例并配置拦截器,能处理 token 和错误
 - ✅ 理解异步请求的本质,能用 async/await 优雅处理
 - ✅ 知道常见坑(CORS、401、超时)的排查方法
 - ✅ 完成至少 6 道练习题,闭卷题得分 ≥ 24/30
 
恭喜!你已具备 Axios 实战能力,可以在项目中独立开发 API 交互模块了! 🎉
