作为一个前端开发者,你是否曾在维护层层嵌套的异步代码时感到绝望?今天,我们通过一段极简的代码,拆解三个核心概念:AJAX、Promise 和封装思想,看看它们如何重塑异步编程。
一、AJAX:异步通信的基石
AJAX(Asynchronous JavaScript and XML)是无需刷新页面就能与服务器交换数据的技术。它的核心工具是 XMLHttpRequest 对象。
传统 AJAX 写法(回调地狱模式)
// 获取 GitHub 用户信息
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://api.github.com/users/xxx', true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
const data = JSON.parse(xhr.responseText);
console.log(data);
// 如果需要嵌套第二个请求
const xhr2 = new XMLHttpRequest();
xhr2.open('GET', data.repos_url, true);
xhr2.onreadystatechange = function() {
// 又一层回调...
};
xhr2.send();
} else {
console.log('请求失败');
}
}
};
xhr.send();
痛点:
- 代码横向增长,逻辑与异步细节混杂
- 错误处理散落在各处
- 难以复用,每次请求都要重复模板代码
二、Promise:异步流程的"状态管理器"
Promise 是 ES6 对异步编程的革命性抽象。它不关心底层用什么技术通信,只专注管理异步操作的状态(pending、fulfilled、rejected)。
Promise 的本质
// 不是具体技术,而是编程范式
const promise = new Promise((resolve, reject) => {
// 内部可以是任何异步操作:AJAX、定时器、文件读取...
});
核心优势:
- 状态固化:一旦 resolve/reject,状态不可变
- 链式调用:
.then().then().catch()消灭嵌套 - 错误冒泡:统一
.catch()捕获整条链的错误
三、封装思想:化繁为简的艺术
回到你的代码,它完美演绎了"封装"的精髓:
const getJSON = (url) => {
return new Promise((resolve, reject) => { // ← Promise 外壳
// 以下全是 AJAX 内核
const xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.send();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
resolve(JSON.parse(xhr.responseText));
}
};
xhr.onerror = () => reject('出错了');
});
};
// 调用时完全感受不到 AJAX 的繁琐
getJSON('https://api.github.com/users/xxx')
.then(data => console.log(data))
.catch(err => console.log(err));
封装的三重境界
表格
| 层次 | 做了什么 | 价值 |
|---|---|---|
| 1. 功能封装 | 把创建 XHR、发请求、解析 JSON 打包成getJSON() | 复用性↑ |
| 2. 模式封装 | 用 Promise 包裹回调,将"事件驱动"转为"状态驱动" | 可读性↑ |
| 3. 接口封装 | 对外只暴露 URL 入参和 then/catch 出参 | 维护性↑ |
一句话总结封装:把"怎么做"藏起来,只告诉用户"做什么"和"能得到什么" 。
四、三者对比:一场跨维度的对话
表格
| 对比维度 | AJAX (技术) | Promise (模式) | 封装后的 AJAX (最佳实践) |
|---|---|---|---|
| 本质 | 浏览器对象:XMLHttpRequest | ES6 类:Promise | 编程思想:抽象与复用 |
| 关注点 | 如何发送 HTTP 请求 | 何时拿到结果 | 怎样优雅地调用 |
| 代码形态 | 事件回调 + 状态判断 | .then().catch() 链 | 函数(url).then() |
| 错误处理 | 分散在 onreadystatechange | 统一 .catch() | 统一 .catch() |
| 可复用性 | ❌ 每次都要重写 | ✅ 可被任何异步复用 | ✅ 一行代码调用 |
| 现代性 | 古老但兼容性好 | 现代异步标准 | 桥梁作用 |
关系图:
底层技术 (AJAX)
↓
被 Promise 包装
↓
通过封装思想抽象
↓
上层接口 (简洁的 getJSON)
五、时代演进:Fetch API 与终极形态
你的代码其实是过渡时期的产物。现代浏览器已内置更优雅的方案:
// Fetch 原生就是 Promise,无需手动封装
fetch('https://api.github.com/users/xxx')
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.log(err));
Fetch = AJAX2.0 + 原生 Promise,它告诉我们:
优秀的封装,最终会被语言本身吸纳为标准。
结语
- AJAX 是枪,提供了火力(异步能力)
- Promise 是瞄准镜,让射击可控(状态管理)
- 封装思想是枪托,让持枪舒适(工程化)
三者的结合,不是简单的叠加,而是一场从具体到抽象、从混乱到秩序的思维升级。下次当你写出 getData().then().catch() 时,记得背后站着这三个巨人。