目录
前言:为什么我们需要关注异步请求?
作为一名 Web 开发者,我们经常面临这样的挑战:如何在用户与网页交互时,动态地获取数据或提交信息,而无需让用户忍受整个页面重新加载带来的闪烁与等待。这正是异步 HTTP 请求技术的用武之地。
在很长一段时间里,AJAX(Asynchronous JavaScript and XML)是解决这个问题的唯一标准。而如今,随着现代 JavaScript 的飞速发展,Fetch API 作为一种基于 Promise 的新标准横空出世。在这篇文章中,我们将深入探讨这两者的核心差异,剖析它们的优缺点,并通过大量的代码示例,帮助你掌握在实际项目中如何做出正确的技术选择。
核心概念对比:AJAX vs Fetch API
在开始写代码之前,让我们先通过一个高层次的对比,来建立对这两种技术的整体认知。AJAX 并不是一种单一的技术,而是一种利用 XMLHttpRequest (XHR) 对象进行异步通信的概念集合;而 Fetch API 则是一个现代的、内置于浏览器原生的 API,旨在简化这一过程。
以下是它们在关键特性上的详细对比:
AJAX (XMLHttpRequest)
:—
依赖于回调函数 和事件监听器。
代码较为冗长,配置项分散。
需要手动处理 INLINECODEd9bf0928 或 INLINECODE92014e47,并依赖 INLINECODE4f565d3f。
.blob()),自动处理数据流解析。 原生支持 INLINECODE1dde2454 事件,可精确监控上传/下载进度。
支持所有浏览器(包括 IE 等老旧版本)。
需同时检查 HTTP 状态码(如 200)和网络错误事件。
深入剖析 AJAX (XMLHttpRequest)
AJAX 的核心是 XMLHttpRequest 对象。虽然现在我们常称之为 XHR,而且也很少再使用 XML 格式传输数据(大部分是 JSON),但这个名字依然流传至今。它是 Web 2.0 时代的基石。
AJAX 的独特优势
- 全能型选手:XHR 提供了非常细粒度的控制。例如,如果你想实现一个文件上传的进度条,AJAX 的
upload.onprogress事件是现成的解决方案。 - 极致的兼容性:如果你的项目需要支持 Internet Explorer 或其他古老的浏览器,AJAX 是你唯一的选择。
- 超时控制:XHR 原生支持
timeout属性,这在处理长时间未响应的请求时非常有用。
AJAX 代码实战:一个经典的数据请求
让我们看看如何使用原生 AJAX 来获取用户数据。为了保持代码的健壮性,我们不仅要处理成功的情况,还要处理错误。
// 定义一个使用 AJAX 获取数据的函数
function getUserDataWithAjax() {
// 1. 创建 XMLHttpRequest 对象
var xhr = new XMLHttpRequest();
// 2. 初始化请求:GET 方法,目标 URL,异步标志
var url = "https://api.example.com/users/1";
xhr.open("GET", url, true);
// 3. 设置响应类型(可选,现代浏览器支持)
xhr.responseType = ‘json‘;
// 4. 注册回调函数:处理状态变化
xhr.onload = function() {
// 检查 HTTP 状态码,200-299 表示成功
if (xhr.status >= 200 && xhr.status < 300) {
// 请求成功,解析数据
console.log("AJAX 成功获取数据:", xhr.response);
} else {
// HTTP 错误(如 404, 500)
console.error("AJAX 请求失败,状态码:", xhr.status);
}
};
// 5. 注册回调函数:处理网络层面的错误
xhr.onerror = function() {
console.error("网络连接发生错误,请检查您的网络。");
};
// 6. 发送请求
xhr.send();
}
// 调用函数
getUserDataWithAjax();
代码解析:
- 我们使用了 INLINECODE0d72d8f8 代替了旧的 INLINECODE8f35cafb,这稍微简化了一些逻辑,但依然需要检查
status来确认请求是否真的成功。 - 注意 INLINECODE2ef94825 仅在网络级别错误(如 DNS 解析失败、断网)时触发,如果服务器返回了 404 错误,INLINECODE4663c625 并不会触发,而是会被 INLINECODE4d3aedc2 捕获,这就是为什么我们必须在 INLINECODE844f3461 里手动判断
xhr.status的原因。
进阶场景:监控上传进度
这是 AJAX 传统上的强项。假设我们需要上传一个大文件并显示进度条:
function uploadFileWithAjax(file) {
var xhr = new XMLHttpRequest();
xhr.open(‘POST‘, ‘/upload-server‘, true);
// 监听上传事件
xhr.upload.onprogress = function(event) {
if (event.lengthComputable) {
var percentComplete = (event.loaded / event.total) * 100;
console.log("上传进度: " + percentComplete + "%");
// 在这里更新 DOM 中的进度条
}
};
xhr.onload = function() {
if (xhr.status === 200) {
console.log("文件上传成功");
}
};
var formData = new FormData();
formData.append(‘file‘, file);
xhr.send(formData);
}
深入剖析 Fetch API
Fetch API 是现代 JavaScript 的首选。它不仅语法更优雅,更重要的是它解决了 XHR 设计上的一些尴尬(比如回调地狱)。它返回一个 Promise,这使得我们可以配合 async/await 使用,写出看起来像同步代码一样的异步逻辑。
Fetch API 的独特优势
- Promise 链式调用:告别深层嵌套的回调函数,代码逻辑线性流动,易于阅读。
- 流操作:Fetch 引入了可读流的概念,允许我们分块处理响应数据,这在处理大文件下载时非常有用。
- 模块化设计:通过 INLINECODEfd922c7f 和 INLINECODEe46e876a 对象,你可以轻松地对请求进行克隆、修改或组合。
Fetch 代码实战:现代写法
让我们用 Fetch API 重写上面的数据请求。我们将使用现代的 INLINECODE47caf800 和 INLINECODE78dc94f8 语法,这是目前最推荐的方式。
// 定义一个异步函数来获取数据
async function getUserDataWithFetch() {
const url = "https://api.example.com/users/1";
try {
// 1. 发起请求,await 等待 Promise 解决
// fetch 返回的是一个 Response 对象
const response = await fetch(url);
// 2. 检查 HTTP 状态码
// 注意:Fetch 不会将 HTTP 错误(如 404)视为 Promise rejection
// 我们必须手动检查 ok 属性或 status
if (!response.ok) {
// 如果状态码不是 2xx,抛出错误
throw new Error(`HTTP 错误! 状态码: ${response.status}`);
}
// 3. 解析 JSON 数据
// response.json() 也是一个异步操作,返回一个 Promise
const data = await response.json();
// 4. 使用数据
console.log("Fetch 成功获取数据:", data);
} catch (error) {
// 5. 捕获网络错误 或我们上面抛出的 HTTP 错误
console.error("发生错误:", error.message);
}
}
getUserDataWithFetch();
重要提示:Fetch 的“坑”
作为经验丰富的开发者,我们必须指出 Fetch 的两个常见陷阱,避免你在实际开发中踩雷:
陷阱 1:HTTP 错误不会触发 Catch
在 INLINECODE85665175 中,只有网络层面的错误(如断网、跨域错误)才会导致 Promise 被拒绝并进入 INLINECODE6eb0203c。如果服务器返回了 404 Not Found 或 500 Server Error,INLINECODE03232c91 返回的 Promise 状态依然是 INLINECODEc9ceebd8。如果不检查 response.ok,你的代码可能会把错误信息当成成功数据来处理,导致严重的 Bug。
陷阱 2:Credentials 和 Cookies
默认情况下,Fetch 发送跨域请求时不会发送 Cookies。如果你需要发送凭证(例如在单页应用中保持登录状态),必须在 options 中显式设置:
fetch(‘https://api.example.com/data‘, {
method: ‘GET‘,
credentials: ‘include‘ // 允许发送 Cookies
});
进阶场景:并发请求与性能优化
在实际开发中,我们经常需要同时发起多个请求,并等待它们全部完成。使用 Promise.all 配合 Fetch,性能远优于顺序发送 AJAX 请求。
async function fetchUserProfile() {
const userId = 1;
// 假设我们需要同时获取:基本信息、订单列表、好友列表
// 这三个请求是独立的,可以并发执行
try {
const [profileRes, ordersRes, friendsRes] = await Promise.all([
fetch(`/api/users/${userId}`),
fetch(`/api/users/${userId}/orders`),
fetch(`/api/users/${userId}/friends`)
]);
// 再次强调:必须检查每个 response 的状态
if (!profileRes.ok || !ordersRes.ok || !friendsRes.ok) {
throw new Error("部分数据加载失败");
}
// 并发解析 JSON(解析操作也是 IO 密集型的,可以并发)
const [profile, orders, friends] = await Promise.all([
profileRes.json(),
ordersRes.json(),
friendsRes.json()
]);
console.log("用户个人资料:", profile);
console.log("订单历史:", orders);
console.log("好友列表:", friends);
} catch (error) {
console.error("加载用户面板时出错:", error);
}
}
错误处理与最佳实践
我们在选择技术时,错误处理机制往往决定了应用的稳定性。
- AJAX 的错误处理:更为直接。INLINECODEb297cecd 处理网络崩溃,INLINECODE83c8ee69 或
onload处理 HTTP 状态。逻辑虽然分散,但对于简单的“获取数据并显示”场景非常直观。 - Fetch 的错误处理:更为集中。我们可以使用一个大的 INLINECODE04ccc2f2 块包裹所有逻辑。但切记要手动封装 INLINECODE61b5cf45 函数,把 INLINECODEb64a1461 的情况标准化为 Error,否则你需要在每次调用时都写一遍重复的 INLINECODEb492a5d6 判断。
推荐的封装实践
为了在实际开发中更高效地使用 Fetch,我们通常会对其进行封装,自动处理 JSON 解析和错误抛出:
// 封装一个通用的 request 函数
async function customFetch(url, options = {}) {
// 默认配置
const defaultOptions = {
headers: {
‘Content-Type‘: ‘application/json‘
}
};
// 合并配置
const finalOptions = { ...defaultOptions, ...options };
try {
const response = await fetch(url, finalOptions);
// 如果 HTTP 状态码不正常,抛出错误
if (!response.ok) {
// 尝试读取服务器返回的错误信息
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.message || `请求失败: ${response.status}`);
}
// 检查响应类型,通常 API 返回 JSON
return await response.json();
} catch (error) {
// 在这里可以集中处理全局错误,例如跳转到登录页
console.error("Request Error:", error);
throw error; // 继续抛出,让调用者也能捕获
}
}
// 使用封装后的函数
async function saveUserSettings(settings) {
try {
const result = await customFetch(‘/api/settings‘, {
method: ‘POST‘,
body: JSON.stringify(settings)
});
console.log("保存成功:", result);
} catch (error) {
alert("保存失败: " + error.message);
}
}
总结:如何做出选择?
我们在文章的最后,来总结一下这两者在实际项目中的应用指南。
AJAX 仍然是特定场景下的王者。如果你正在维护一个老旧的系统,或者你需要支持非常旧的浏览器(如 IE),或者你需要处理带有精确进度条的大文件上传,AJAX 是不可替代的。
然而,对于大多数现代 Web 开发,Fetch API 是赢家。它的语法更清晰,与 async/await 的结合让它成为异步处理的自然选择。更重要的是,Fetch 不仅仅是一个“更简单的 XHR”,它是基于 Streams 构建的,代表了 Web 通信的未来方向。
通过这篇文章,我们希望你不仅理解了它们在技术实现上的区别,更掌握了在面对复杂业务场景时,如何权衡利弊,选择最得心应手的工具。现在,打开你的编辑器,试着用 INLINECODEd8bd90df 去重构那些陈旧的 INLINECODE14c50e96 代码吧,你会发现代码的整洁度提升了一个档次!