AJAX、Promise 与封装思想:从回调地狱到优雅异步编程

60 阅读3分钟

作为一个前端开发者,你是否曾在维护层层嵌套的异步代码时感到绝望?今天,我们通过一段极简的代码,拆解三个核心概念:AJAXPromise封装思想,看看它们如何重塑异步编程。


一、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、定时器、文件读取...
});

核心优势

  1. 状态固化:一旦 resolve/reject,状态不可变
  2. 链式调用.then().then().catch() 消灭嵌套
  3. 错误冒泡:统一 .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 (最佳实践)
本质浏览器对象:XMLHttpRequestES6 类: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() 时,记得背后站着这三个巨人。