深入探索 JavaScript 中的网络请求:从 XHR 到现代 Async/Await

作为一名在这个行业摸爬滚打多年的开发者,我们都见证了 Web 技术的飞速迭代。在现代 Web 开发中,与服务器进行通信无疑是不可或缺的核心能力。无论是获取海量用户数据、提交复杂的交互表单,还是在 2026 年这个 AI 驱动的时代实时流式传输大模型(LLM)的响应 Token,我们都需要依赖 JavaScript 来发起高效、稳健的网络请求。

在这篇文章中,我们将不仅仅停留在基础语法的教学上,而是会以 2026 年的前端视角,深入探讨 JavaScript 中发起请求的四种主要方式,并融入现代工程化理念。我们将从传统的 INLINECODEc343a014 开始回顾历史,过渡到现代浏览器内置的 INLINECODEbdcb4998,探索强大的第三方库 INLINECODEef2df79a,最后掌握优雅的 INLINECODE7cfd0328 语法。此外,我们还将特别讨论在 AI 原生应用浪潮下的请求处理新范式。

JavaScript XMLHttpRequest (XHR)

历史背景与遗留系统维护

XMLHttpRequest(简称 XHR)是 JavaScript 中与服务器交互的“鼻祖”。虽然现在有了更现代的工具,但在我们维护一些 2010 年代遗留的企业级 ERP 系统或老旧框架时,依然能见到它的身影。了解 XHR 不仅是致敬历史,更是为了在系统重构时不踩坑。

核心概念与现代陷阱

XHR 是一个内置的 JavaScript 对象。它的名字虽然包含“XML”,但实际上它可以请求任何类型的数据。然而,在 2026 年的开发规范中,如果我们必须维护 XHR 代码,最大的风险在于其默认的异步行为处理。

代码实现与错误边界

基本的 XHR 请求通常遵循以下步骤。让我们来看一个包含完整错误处理逻辑的实际案例:

// 1. 创建一个新的 XMLHttpRequest 对象
const xhr = new XMLHttpRequest();

// 2. 配置请求
const requestType = ‘GET‘;
const apiUrl = ‘https://api.legacy-system.com/data‘;

// 第三个参数 "true" 代表异步。极重要:在 2026 年,严禁使用 false (同步请求)
// 同步请求会阻塞主线程,导致 UI 冻结,用户体验极差
xhr.open(requestType, apiUrl, true);

// 3. 设置响应类型
xhr.responseType = ‘json‘;

// 4. 处理响应状态变化 (这是 XHR 最繁琐的地方)
xhr.onreadystatechange = function () {
  // readyState 4 表示请求完成
  if (xhr.readyState === 4) {
    // 检查状态码,200-299 表示成功
    if (xhr.status >= 200 && xhr.status < 300) {
      console.log('获取到的数据:', xhr.response);
    } else {
      // 处理 HTTP 错误,如 404 或 500
      console.error('业务逻辑错误,状态码:', xhr.status);
    }
  }
};

// 5. 处理网络层面的错误 (如 DNS 解析失败、断网)
xhr.onerror = function() {
  console.error('网络层连接失败');
};

// 6. 超时处理 (老式 API 的必要手段)
xhr.ontimeout = function() {
  console.error('请求超时');
};
xhr.timeout = 5000; // 设置 5 秒超时

// 7. 发送请求
xhr.send();

为什么我们在新项目中不再推荐它

正如你在上面的代码中看到的,XHR 的 API 设计基于回调。在处理复杂的异步操作流(比如请求 A 完成后依赖结果请求 B)时,这会导致严重的“回调地狱”。此外,它没有内置的 Promise 支持,无法直接配合 async/await 使用,这使得代码可读性和可维护性大打折扣。

JavaScript Fetch API

现代浏览器的原生标准

INLINECODEbfb9864f API 是现代浏览器内置的原生 API,旨在取代 INLINECODEa3709371。它基于 Promise 设计,使得处理异步请求变得更加简洁。特别是在 2026 年,随着 Edge Computing(边缘计算)的普及,原生的 fetch 在 Service Worker 和边缘脚本中发挥着重要作用。

Fetch 的“双刃剑”:HTTP 错误不抛出异常

这是许多新手甚至资深开发者容易忽视的陷阱:INLINECODEcbcdf6c6 只有在网络故障(如断网)或请求被阻止时才会 reject(进入 INLINECODEac0bfb53)。对于 404 (Not Found) 或 500 (Server Error) 这样的 HTTP 错误,它依然会 resolve。我们必须手动检查 response.ok

实战:流式处理与 AI 应用

在 AI 时代,我们经常需要处理流式响应。INLINECODE2023f70a 原生支持 ReadableStream,这是 Axios 目前较难原生支持的优势。让我们来看一个如何使用 INLINECODE192aa162 消费 OpenAI 兼容接口流式响应的例子:

async function fetchAIStream(prompt) {
  const url = ‘https://api.example.com/v1/chat/completions‘;
  
  try {
    const response = await fetch(url, {
      method: ‘POST‘,
      headers: {
        ‘Content-Type‘: ‘application/json‘,
        ‘Authorization‘: ‘Bearer YOUR_API_KEY‘
      },
      body: JSON.stringify({
        model: ‘gpt-2026‘,
        messages: [{ role: ‘user‘, content: prompt }],
        stream: true // 开启流式传输
      })
    });

    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    // 获取 reader
    const reader = response.body.getReader();
    const decoder = new TextDecoder(‘utf-8‘);

    while (true) {
      const { done, value } = await reader.read();
      if (done) break;
      
      // 解码数据块并实时渲染
      const chunk = decoder.decode(value, { stream: true });
      console.log(‘收到 Token:‘, chunk);
      // 这里你可以将 chunk 追加到页面的 UI 中
    }

  } catch (error) {
    console.error(‘流式请求失败:‘, error);
  }
}

中断请求:AbortController 的艺术

在 2026 年,用户对交互的即时性要求极高。如果用户跳转了页面,我们必须取消正在进行的请求以节省带宽和服务器资源。INLINECODE2a5aa7c7 配合 INLINECODEa21741ce 是最佳实践:

const controller = new AbortController();
const signal = controller.signal;

// 发起请求
fetch(‘https://api.example.com/slow-query‘, { signal })
  .then(response => response.json())
  .then(data => console.log(data))
  .catch(err => {
    if (err.name === ‘AbortError‘) {
      console.log(‘请求已被主动终止‘);
    } else {
      console.error(‘请求出错:‘, err);
    }
  });

// 假设用户点击了“取消”按钮
// document.getElementById(‘cancelBtn‘).onclick = () => controller.abort();

JavaScript Axios

企业级开发的首选

尽管 fetch 很强大,但在大型团队协作和企业级项目中,我们往往更青睐 Axios。为什么?因为它不仅自动处理了 JSON 数据转换,还提供了极为强大的拦截器机制。这对于我们统一处理 Token 注入、全局错误日志上报和请求重试逻辑至关重要。

拦截器:高级开发者的利器

让我们来看一个我们在近期一个金融科技项目中使用的 Axios 配置案例。它展示了如何通过拦截器实现自动化鉴权和错误处理:

import axios from ‘axios‘;

// 创建一个 axios 实例
const apiClient = axios.create({
  baseURL: ‘https://api.fintech-prod.com/v1/‘,
  timeout: 10000,
  headers: { ‘Content-Type‘: ‘application/json‘ }
});

// 请求拦截器:在请求发送前执行
apiClient.interceptors.request.use(
  (config) => {
    // 比如从本地存储动态获取最新的 Token
    const token = localStorage.getItem(‘auth_token‘);
    if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    
    // 在 2026 年,我们可能会为每个请求添加一个 Request ID 用于链路追踪
    config.headers[‘X-Request-ID‘] = crypto.randomUUID();
    
    return config;
  },
  (error) => {
    // 处理请求配置错误
    return Promise.reject(error);
  }
);

// 响应拦截器:统一处理错误和 Token 过期
apiClient.interceptors.response.use(
  (response) => {
    // 只返回核心数据,减少业务代码中的冗余访问
    return response.data;
  },
  async (error) => {
    const originalRequest = error.config;

    // 处理 401 未授权错误:自动刷新 Token 并重试
    if (error.response && error.response.status === 401 && !originalRequest._retry) {
      originalRequest._retry = true;
      
      try {
        const newToken = await refreshToken(); // 假设有一个刷新 Token 的方法
        localStorage.setItem(‘auth_token‘, newToken);
        
        // 更新原始请求的 Header
        apiClient.defaults.headers.common[‘Authorization‘] = `Bearer ${newToken}`;
        
        // 关键:返回重试的请求,让用户无感知
        return apiClient(originalRequest);
      } catch (refreshError) {
        // 刷新失败,跳转到登录页
        window.location.href = ‘/login‘;
        return Promise.reject(refreshError);
      }
    }

    return Promise.reject(error);
  }
);

export default apiClient;

并发与性能优化

当我们需要加载大量不相关的数据时,串行等待是性能杀手。Axios 提供了 INLINECODE8d9af312(本质上是 INLINECODE74a0a895 的封装)来并发执行请求。

import axios from ‘axios‘;

async function loadDashboardData() {
  try {
    // 并行发起请求,等待所有完成
    const [userProfile, notifications, settings] = await Promise.all([
      axios.get(‘/user/profile‘),
      axios.get(‘/notifications‘),
      axios.get(‘/settings‘)
    ]);

    console.log(‘所有数据加载完成‘);

  } catch (error) {
    console.error(‘部分数据加载失败:‘, error);
  }
}

JavaScript Async/Await

让代码回归线性思维

async/await 并不是一种新的请求技术,而是处理 Promise 的语法糖。但在 2026 年,它已成为编写异步代码的唯一推荐标准。它允许我们用同步的方式编写异步代码,彻底摆脱回调函数的嵌套,极大地提升了代码的可读性,也方便 AI 辅助编程工具(如 GitHub Copilot)理解我们的意图。

实战:编写健壮的异步函数

让我们将前面的知识融合起来,编写一个符合生产级标准的 async/await 函数。在这个例子中,我们将展示如何优雅地处理重试逻辑和超时控制。

// 封装一个带有超时控制的 Fetch 函数
function fetchWithTimeout(resource, options = {}) {
  const { timeout = 8000 } = options;
  
  const controller = new AbortController();
  const id = setTimeout(() => controller.abort(), timeout);
  
  return fetch(resource, {
    ...options,
    signal: controller.signal
  }).finally(() => {
    clearTimeout(id);
  });
}

async function getSecureData(url) {
  // 总是使用 try...catch 包裹 await 逻辑
  try {
    // 等待请求完成
    const response = await fetchWithTimeout(url, {
      method: ‘GET‘,
      headers: { ‘Accept‘: ‘application/json‘ }
    });

    // 检查业务逻辑错误
    if (!response.ok) {
      // 抛出一个包含状态码的详细错误
      throw new Error(`HTTP Status: ${response.status}`);
    }

    // 解析 JSON
    const data = await response.json();
    
    // 这里可以加入数据验证逻辑
    if (!data || typeof data !== ‘object‘) {
      throw new Error(‘Invalid data format received from server‘);
    }

    return data;

  } catch (error) {
    // 集中处理所有类型的错误
    if (error.name === ‘AbortError‘) {
      console.error(‘请求超时,请检查网络连接‘);
    } else {
      console.error(‘数据加载失败:‘, error.message);
    }
    
    // 视情况决定是否将错误继续抛出,或者返回 null/降级数据
    return null;
  }
}

// 调用
const userData = await getSecureData(‘https://api.example.com/me‘);

最佳实践总结

  • 总是使用 INLINECODE2beb8a3f: INLINECODE26261b90 只有在 try 块中才能优雅地捕获错误。不要让未处理的 Promise rejection 泄漏出去,这会导致 Node.js 进程在旧版本中崩溃,或在浏览器中产生难以调试的控制台警告。
  • 避免串行地狱: 仔细检查你的代码。如果后一个请求不依赖前一个请求的结果,请务必使用 Promise.all 并行执行。这在加载数据仪表盘时能带来数倍的性能提升。
  • 关注可观测性: 在生产环境中,我们会在拦截器或 catch 块中自动上报错误日志到监控系统(如 Sentry 或 DataDog)。

2026 开发者视角的总结

在文章的最后,让我们回顾一下这四种工具的定位。作为一名紧跟技术前沿的开发者,我们不仅要会写代码,更要有技术选型的决策力。

  • XMLHttpRequest: 除非你在维护遗留系统,否则避免使用。它是过去时代的产物,其回调机制容易产生难以维护的代码。
  • Fetch API: 浏览器原生的未来标准。如果你正在开发轻量级应用、库,或者需要处理 ReadableStream(如 AI 流式对话),Fetch 是不二之选。它无需引入额外的包体积。
  • Axios: 企业级应用的中流砥柱。它的拦截器、自动 JSON 转化、更友好的错误处理以及对旧浏览器的兼容性,使得它在大型团队项目中依然占据主导地位。
  • Async/Await: 代码的“语法糖”,但也是“必需品”。无论你选择 Fetch 还是 Axios,都请配合 async/await 使用。它让代码逻辑更加线性,降低了认知负荷,也更容易进行 AI 辅助重构。

希望这篇指南能帮助你构建出既健壮又现代化的前端应用。在不断变化的 Web 技术浪潮中,掌握这些基础与进阶技巧,是我们应对未来挑战的基石。

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。如需转载,请注明文章出处豆丁博客和来源网址。https://shluqu.cn/17579.html
点赞
0.00 平均评分 (0% 分数) - 0