如何使用 JavaScript 发起 HTTP 请求:从基础到实战指南

在当今的 Web 开发领域,数据就是一切。你是否想过,当你在浏览器中刷新社交媒体动态,或者在电商网站搜索商品时,这些数据是如何神奇地出现在你的屏幕上的?这背后离不开一项核心技术:发起 HTTP 请求

作为一名开发者,掌握如何在 JavaScript 中高效地与服务器进行通信是必不可少的技能。站在 2026 年的视角,我们不仅是在请求 API,更是在构建连接前端与 AI 驱动后端的桥梁。在这篇文章中,我们将深入探讨 JavaScript 中发起 HTTP 请求的主流方法。我们将一起从经典的技术出发,逐步过渡到现代的标准解决方案,甚至展望未来趋势,通过丰富的实战代码示例,带你全面理解这一核心技能。

现代开发范式:从 2026 年看 HTTP 请求

在我们深入代码之前,让我们先聊聊当下的开发环境。如果你现在刚开始学习编程,你可能会觉得 INLINECODE57b42b6d 已经足够简单了。但在我们最近的企业级项目中,仅仅会写 INLINECODEb774f36b 是远远不够的。

现在的开发环境发生了巨大的变化:

  • AI 辅助编程(AI-Native DX):我们现在使用像 Cursor 或 Windsurf 这样的 AI 智能体(Agent)来辅助我们编写请求逻辑。我们不再手动拼写每一个 URL,而是描述意图,让 AI 生成类型安全的请求代码。
  • 边缘计算与 Serverless:我们的前端代码可能运行在离用户最近的边缘节点上,这意味着请求的延迟和容错处理变得更加关键。
  • 响应式与流式数据:传统的“请求-响应”模型正在向流式传输转变,特别是在处理 AI 大模型(LLM)的文本生成时,我们需要处理流式响应,而不仅仅是等待 JSON 完成。

尽管技术在变,但核心原理依然稳固。让我们先打好基础。

深入探讨:Fetch API 的现代实践

fetch 已经成为现代 Web 开发的基石。在 Node.js 18+ 原生支持之后,它真正实现了“一次编写,到处运行”。但要在 2026 年写出生产级的代码,我们需要对它进行更深层次的封装。

为什么原生 Fetch 还不够?

虽然 fetch 很强大,但在实际工程中,它有一些让我们头疼的“坑”:

  • 它不会自动处理 HTTP 错误:INLINECODE7b5a949e 只有在网络错误时 reject Promise,对于 404 或 500 错误,它依然会 resolve,导致我们需要手动检查 INLINECODEec13800e。
  • 超时控制缺失:原生的 fetch 没有超时参数,在网络不稳定时,请求可能会无限期挂起。
  • 请求取消繁琐:使用 AbortController 虽然标准,但代码略显冗余。

实战示例 1:构建一个生产级的请求包装器

让我们不依赖任何第三方库(如 Axios),仅用原生技术构建一个符合 2026 年标准的请求工具。我们会加入超时控制、自动 Token 刷新机制以及更完善的错误拦截。

// utils/request.js - 生产级 Fetch 封装

/**
 * 发起带有超时和错误处理能力的 HTTP 请求
 * @param {string} url - 请求地址
 * @param {object} options - fetch 配置项
 * @param {number} timeout - 超时时间(毫秒),默认 5000ms
 */
export async function request(url, options = {}, timeout = 5000) {
    // 1. 创建 AbortController 用于取消请求(支持超时)
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), timeout);

    // 2. 获取 Token(模拟从 AuthStore 或 Cookie 获取)
    // 在现代应用中,这可能是一个用于调用下游微服务的 JWT
    const token = localStorage.getItem(‘auth_token‘); 
    
    // 默认配置:包含凭证和标准头
    const defaults = {
        method: ‘GET‘,
        headers: {
            ‘Content-Type‘: ‘application/json‘,
            ‘Authorization‘: token ? `Bearer ${token}` : ‘‘,
            // 2026年常见的 AIGC 请求头,标识客户端意图
            ‘X-Client-Intent‘: ‘data-fetch‘
        },
        credentials: ‘include‘, // 支持跨域 Cookie
    };

    // 合并配置
    const config = { ...defaults, ...options, signal: controller.signal };
    
    // 如果是 GET 请求,确保没有 body;如果是其他请求,确保 body 序列化
    if (config.body && typeof config.body === ‘object‘) {
        config.body = JSON.stringify(config.body);
    }

    try {
        console.log(`[Request] ${config.method} ${url}`); // 可观测性日志
        
        const response = await fetch(url, config);
        
        // 清除超时计时器
        clearTimeout(timeoutId);

        // 3. 统一处理 HTTP 状态码错误
        // 这一点非常关键:fetch 不会把 404 当作错误抛出,我们必须手动处理
        if (!response.ok) {
            // 尝试解析后端返回的错误信息(如 { "error": "Invalid token" })
            const errorData = await response.json().catch(() => ({}));
            throw new Error(`HTTP Error ${response.status}: ${errorData.message || response.statusText}`);
        }

        // 4. 处理 204 No Content
        if (response.status === 204) {
            return null;
        }

        return await response.json();

    } catch (error) {
        // 处理超时导致的 AbortError
        if (error.name === ‘AbortError‘) {
            throw new Error(`请求超时: 请在 ${timeout}ms 内完成`);
        }
        // 在这里可以接入 Sentry 或其他监控工具
        console.error(‘[Network Error]‘, error);
        throw error; // 重新抛出,让上层业务逻辑处理
    }
}

代码深度解析:

在这个例子中,我们做了几件看似微小但至关重要的事情。首先,我们引入了 INLINECODEde0872fc,这是 2026 年处理请求取消的标准方式,它不仅能用于用户手动取消,还能实现精确的超时控制。其次,我们在 Headers 中加入了 INLINECODE540180ad 和 INLINECODE90a58990 的自动化处理,这消除了在每个 API 调用中重复编写样板代码的需要。最后,也是最容易被忽视的一点,我们明确处理了 INLINECODEc5a5cd2e 的情况。记住,Fetch 网络成功不代表业务成功,这是新手最容易掉进的陷阱。

实战示例 2:处理 AI 流式响应

随着生成式 AI 的普及,我们经常不再等待整个请求完成,而是处理“流式”数据。比如,我们正在接入一个后台 AI 助理,需要像 ChatGPT 那样逐字显示回复。

// 示例:如何处理流式读取
async function fetchAIResponse(prompt) {
    try {
        const response = await fetch(‘/api/ai/generate‘, {
            method: ‘POST‘,
            body: JSON.stringify({ prompt }),
            headers: { ‘Content-Type‘: ‘application/json‘ }
        });

        // 检查是否支持流式传输
        if (!response.body) {
            throw new Error(‘ReadableStream not supported‘);
        }

        const reader = response.body.getReader();
        const decoder = new TextDecoder(‘utf-8‘);
        let result = ‘‘;

        while (true) {
            // 读取数据流的一个片段
            const { done, value } = await reader.read();
            
            if (done) break;
            
            // 解码并追加数据
            const chunk = decoder.decode(value, { stream: true });
            result += chunk;
            
            // 实时更新 UI(例如打字机效果)
            document.getElementById(‘ai-output‘).textContent = result;
        }
        
        return result;
    } catch (error) {
        console.error(‘AI Stream Failed:‘, error);
    }
}

在这个例子中,我们使用了 response.body.getReader()。这是现代浏览器的强大特性,允许我们在数据下载完成之前就开始处理它。对于 AI 应用或者超大文件下载,这能极大地提升用户的感知速度。

经典回顾:XMLHttpRequest (XHR) 的现实意义

既然我们已经有了 Fetch 和各种现代化的库,为什么还要谈论 XHR?如果你正在维护一个遗留的银行系统或者一个大型的旧版 ERP 系统,你会发现 XHR 依然无处不在。此外,XHR 提供了一些 Fetch 至今仍难以完美替代的特性,比如上传进度的精确监控

实战示例 3:使用 XHR 实现文件上传进度条

Fetch API 目前原生不支持读取上传进度(只能通过低级流控制),而 XHR 可以轻松做到。这是我们仍然需要掌握它的主要场景之一。


上传进度: 0%

function uploadFile(file) { return new Promise((resolve, reject) => { const xhr = new XMLHttpRequest(); // 监听上传进度事件 xhr.upload.addEventListener(‘progress‘, (e) => { if (e.lengthComputable) { const percentComplete = (e.loaded / e.total) * 100; document.getElementById(‘progress‘).textContent = percentComplete.toFixed(2) + ‘%‘; // 在这里我们可以平滑地更新 CSS 宽度 } }); // 监听完成事件 xhr.addEventListener(‘load‘, () => { if (xhr.status >= 200 && xhr.status { if(e.target.files.length > 0) { uploadFile(e.target.files[0]).catch(console.error); } });

在这个案例中,xhr.upload.onprogress 是独一无二的。在 2026 年,虽然 WebSockets 和其他协议也很流行,但在处理标准的表单文件上传时,XHR 依然是那个最简单、最可靠的“老伙计”。

2026 前端工程化:请求层的高级治理

在我们最近的一个大型电商重构项目中,我们意识到仅仅会发请求是不够的,我们需要治理请求。这涉及到了性能优化、缓存策略以及与边缘节点的协同。

1. 去重与缓存

在一个复杂的单页应用(SPA)中,用户可能会在极短的时间内多次点击同一个按钮,或者多个组件同时渲染并发起相同的 API 请求。这被称为“请求瀑布”。

解决方案: 我们可以在内存中维护一个 Pending 请求映射。

// 简单的内存缓存与去重包装器
const pendingRequests = new Map();

export async function cachedRequest(url, options) {
    // 生成请求的唯一标识
    const requestKey = `${options.method || ‘GET‘}-${url}`;

    // 如果这个请求正在进行中,直接返回同一个 Promise
    if (pendingRequests.has(requestKey)) {
        console.log(`[Dedup] 复用现有请求: ${requestKey}`);
        return pendingRequests.get(requestKey);
    }

    // 创建新的请求 Promise
    const requestPromise = request(url, options)
        .finally(() => {
            // 请求完成后,无论成功失败,都要从 Map 中移除
            pendingRequests.delete(requestKey);
        });

    // 存入 Map
    pendingRequests.set(requestKey, requestPromise);

    return requestPromise;
}

这种模式在处理高并发场景时极其有效。想象一下,用户疯狂点击“刷新”按钮,有了这段代码,我们实际上只发送了一次网络请求,其他的点击都共享了同一个结果。

2. 性能监控与可观测性

在现代前端中,知道“请求失败了”是不够的,我们需要知道“为什么失败”以及“用户体验如何”。我们可以利用 PerformanceObserver 来监控资源加载时间。

// 监控特定资源的加载时间
const observer = new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
        if (entry.entryType === ‘resource‘) {
            // 发送数据到监控平台(如 Sentry 或 DataDog)
            if (entry.duration > 1000) {
                console.warn(`[Perf Alert] API ${entry.name} took ${entry.duration}ms`);
            }
        }
    }
});

observer.observe({ entryTypes: [‘resource‘] });

技术选型:Fetch vs XHR vs Axios

到了 2026 年,我们该如何选择工具?

  • Fetch API: 首选。它是标准,被所有现代浏览器和 Runtimes(Deno, Bun, Node)支持。代码少,且原生支持流。除非你需要极老的浏览器兼容,否则这是默认选择。
  • XMLHttpRequest: 特定场景。仅在需要上传进度监听、或者在维护无法改动构建工具的旧项目时使用。
  • Axios / 其他库: 依然强大。虽然 Fetch 已经很好,但 Axios 依然提供了诸如请求拦截器、自动 JSON 转换、更宽泛的旧浏览器支持等便利。在大型团队中,使用 Axios 可以统一 API 调用的风格,减少心智负担。但在我们看来,如果你能写好一个 request 包装器,原生 Fetch 已经足够应对 99% 的需求。

总结

在这篇文章中,我们穿越了技术演进的河流。从古老的 INLINECODE714a2d9a 到标准的 INLINECODE38e656dd,再到流式处理和请求治理。

我们希望你现在明白,发起 HTTP 请求不仅仅是调用一个函数,而是构建应用神经系统的一部分。无论你是为了构建下一个颠覆性的 AI 应用,还是为了维护关键的企业系统,掌握这些底层原理和进阶技巧,都将是你 2026 年技术栈中最坚实的基石。

接下来,我们建议你尝试在你的下一个项目中,亲手封装一个包含超时、去重和错误处理的 request 函数。相信我,你的代码会因此变得更加优雅和健壮。

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