Promise包装Ajax的核心是将XMLHttpRequest异步流程转为Promise链式调用,提升可读性并支持async/await;手动封装需在Promise中处理onload/onerror,fetch版需检查response.ok,生产环境推荐axios等成熟库。

用 Promise 包装 Ajax,核心是把 XMLHttpRequest 的异步流程转换成标准的 Promise 链式调用,让请求更简洁、可读性更强,也方便配合 async/await 使用。
手动封装一个基于 XMLHttpRequest 的 Promise 版 Ajax
这是最基础也最能理解原理的方式:在 Promise 构造函数中发起请求,根据 onload 和 onerror 分别调用 resolve 或 reject:
function ajax(options) {
return new Promise((resolve, reject) => {
const xhr = new XMLHttpRequest();
const { url, method = 'GET', headers = {}, data = null } = options;
xhr.open(method, url);
Object.keys(headers).forEach(key => {
xhr.setRequestHeader(key, headers[key]);
});
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
try {
const json = JSON.parse(xhr.responseText);
resolve(json);
} catch (e) {
resolve(xhr.responseText); // 非 JSON 响应直接返回文本
}
} else {
reject(new Error(`HTTP ${xhr.status}: ${xhr.statusText}`));
}
};
xhr.onerror = () => reject(new Error('Network error'));
xhr.ontimeout = () => reject(new Error('Request timeout'));
xhr.timeout = options.timeout || 10000;
if (method === 'GET' || !data) {
xhr.send();
} else if (typeof data === 'object') {
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.send(JSON.stringify(data));
} else {
xhr.send(data);
}
});
}
使用示例:
ajax({ url: '/api/users' })
.then(data => console.log(data))
.catch(err => console.error(err));
// 或配合 async/await
async function fetchUsers() {
try {
const users = await ajax({ url: '/api/users' });
console.log(users);
} catch (err) {
console.error(err);
}
}
用 fetch 替代 XMLHttpRequest(更现代、更推荐)
fetch 本身已返回 Promise,但默认不拒绝 HTTP 错误状态(如 404、500),需要手动判断 response.ok:
立即学习“Java免费学习笔记(深入)”;
function ajax(options) {
const { url, method = 'GET', headers = {}, body = null, timeout = 10000 } = options;
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);
return fetch(url, {
method,
headers: {
'Content-Type': 'application/json',
...headers
},
body: method !== 'GET' && body ? JSON.stringify(body) : null,
signal: controller.signal
})
.then(res => {
clearTimeout(id);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return res.json(); // 自动解析 JSON;若需响应文本可用 res.text()
})
.catch(err => {
clearTimeout(id);
if (err.name === 'AbortError') throw new Error('Request timeout');
throw err;
});
}
支持 GET 参数和 POST 表单提交的增强版
实际项目中常需拼接 URL 参数或发送表单数据,可在封装时增加处理逻辑:
- GET 请求自动序列化
params对象为查询字符串(如{id: 1, name: 'a'}→?id=1&name=a) - POST 提交
FormData时不设Content-Type,让浏览器自动设置边界 - 对
body类型做判断:对象 → JSON,FormData→ 原样传,字符串 → 直接发
配合 axios 等库更省心(非必须,但实用)
虽然原生封装有助于理解,但生产环境推荐用成熟库。例如 axios 默认返回 Promise,自动转换 JSON,支持拦截器、取消请求、请求重试等:
import axios from 'axios';
// 所有请求都返回 Promise
axios.get('/api/users')
.then(res => res.data)
.catch(err => console.error(err));
// 或 await
const users = await axios.post('/api/users', { name: 'Alice' }).then(r => r.data);
它内部就是基于 XMLHttpRequest(浏览器)或 http(Node)+ Promise 封装的,无需重复造轮子。


















