在 Promise 出现之前,我们写 AJAX 是这样的:
// 传统回调写法(回调地狱预警)
const xhr = new XMLHttpRequest();
xhr.open("GET", "url1", true);
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
// 第一个请求成功,再发第二个请求
const xhr2 = new XMLHttpRequest();
xhr2.open("GET", "url2", true);
xhr2.onreadystatechange = function() {
if (xhr2.readyState === 4 && xhr2.status === 200) {
// 第二个请求成功,再发第三个...
}
};
xhr2.send();
}
};
xhr.send();
「AJAX 是异步操作(发请求→等响应→出结果),Promise 正好用 3 个状态对应这个流程:」
- 发送 AJAX 请求后 → Promise 处于「pending 状态」(等待中);
- AJAX 请求成功(状态码 200)→ Promise 转为「fulfilled 状态」,调用
resolve传递数据; - AJAX 请求失败(状态码非 200 / 网络错误)→ Promise 转为「rejected 状态」,调用
reject传递错误。
用 Promise 的状态,记录 AJAX 的异步结果,后续通过.then()/.catch()获取结果,不用嵌套回调
// 封装函数:传入URL,返回Promise对象
function getJSON(url) {
// 1. 创建Promise对象,包裹AJAX异步操作
return new Promise(function(resolve, reject) {
// 2. 初始化AJAX核心对象(XMLHttpRequest)
const xhr = new XMLHttpRequest();
// 3. 配置请求:方法(GET)、URL、异步(true)
xhr.open("GET", url, true);
// 4. 监听请求状态变化(核心:判断请求是否完成)
xhr.onreadystatechange = function() {
// readyState是AJAX的状态:4表示请求已完成(不管成功/失败)
if (xhr.readyState !== 4) return; // 未完成则直接返回,不处理
// 5. 请求完成后,判断响应状态码
if (xhr.status === 200) {
// 状态码200 = 成功 → 调用resolve,传递响应数据
resolve(xhr.response); // response是JSON对象(后续配置)
} else {
// 状态码非200 = 失败 → 调用reject,传递错误信息
reject(new Error(`请求失败:${xhr.statusText}`));
}
};
// 6. 监听网络错误(比如断网、跨域)
xhr.onerror = function() {
reject(new Error("网络错误,请求失败"));
};
// 7. 配置响应格式:自动把服务器返回的字符串转成JSON对象
xhr.responseType = "json";
// 8. 设置请求头:告诉服务器“我要JSON格式的数据”
xhr.setRequestHeader("Accept", "application/json");
// 9. 发送请求(GET请求没有请求体,传null)
xhr.send(null);
});
}
// 示例:请求JSONPlaceholder的测试接口
getJSON("https://jsonplaceholder.typicode.com/todos/1")
// 成功回调:拿到JSON数据
.then((data) => {
console.log("请求成功,数据:", data);
// 实际开发:渲染数据到页面
// document.getElementById("todo-title").innerText = data.title;
return data.userId; // 传递数据给下一个then(链式调用)
})
// 链式调用:用第一个请求的结果发第二个请求
.then((userId) => {
// 用userId请求用户信息
return getJSON(`https://jsonplaceholder.typicode.com/users/${userId}`);
})
.then((userData) => {
console.log("用户信息:", userData);
})
// 捕获所有失败(第一个/第二个请求失败都走这里)
.catch((err) => {
console.error(err.message); // 统一处理错误
})
// 无论成功/失败,都会执行(比如关闭loading动画)
.finally(() => {
console.log("请求结束,关闭loading");
});
基础版只支持 GET,实际开发中需要 POST(比如提交表单),我们扩展成通用版ajax函数:
// 通用版AJAX封装:支持GET/POST,可传请求体、请求头
function ajax(options) {
// 默认参数:method默认GET,data默认null,headers默认空对象
const {
url,
method = "GET",
data = null,
headers = {}
} = options;
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open(method.toUpperCase(), url, true); // 方法转大写,避免大小写问题
// 设置请求头(支持自定义,比如Token)
Object.keys(headers).forEach((key) => {
xhr.setRequestHeader(key, headers[key]);
});
// 监听状态变化
xhr.onreadystatechange = function() {
if (xhr.readyState !== 4) return;
if (xhr.status >= 200 && xhr.status < 300) { // 2xx都是成功状态码
resolve(xhr.response);
} else {
reject(new Error(`请求失败:${xhr.statusText}`));
}
};
xhr.onerror = () => reject(new Error("网络错误"));
xhr.responseType = "json"; // 仍支持JSON响应
// 处理请求体:POST请求需要把data转成字符串
const requestData = data ? JSON.stringify(data) : null;
// 如果是POST,默认设置Content-Type(表单提交/JSON提交)
if (method.toUpperCase() === "POST" && !headers["Content-Type"]) {
xhr.setRequestHeader("Content-Type", "application/json");
}
xhr.send(requestData); // 发送请求体(POST用)
});
}
// 示例:提交表单数据(POST请求)
ajax({
url: "https://jsonplaceholder.typicode.com/posts",
method: "POST",
data: {
title: "前端Promise实战",
body: "用Promise封装AJAX真好用",
userId: 1
},
headers: {
// 自定义请求头(比如Token)
// "Authorization": "Bearer your-token"
}
})
.then((res) => console.log("提交成功:", res))
.catch((err) => console.error(err.message));