手写实现Ajax
从零实现一个支持Promise的Ajax函数,掌握XMLHttpRequest的用法
问题
Ajax(Asynchronous JavaScript and XML)是前端与后端进行异步数据交互的技术。这道题要求我们手写实现一个Ajax函数,需要:
- 封装原生的XMLHttpRequest对象
- 支持GET、POST等常见HTTP方法
- 支持Promise风格的异步调用
- 能够设置请求头、超时时间等配置
- 正确处理响应数据和错误情况
解答
/**
* 手写实现Ajax
* @param {Object} options 配置对象
* @param {string} options.url 请求地址
* @param {string} options.method 请求方法,默认GET
* @param {Object} options.data 请求数据
* @param {Object} options.headers 请求头
* @param {number} options.timeout 超时时间(毫秒)
* @param {string} options.responseType 响应类型
* @returns {Promise}
*/
function ajax(options) {
return new Promise((resolve, reject) => {
// 默认配置
const {
url,
method = 'GET',
data = null,
headers = {},
timeout = 0,
responseType = 'json'
} = options;
// 创建XMLHttpRequest对象
const xhr = new XMLHttpRequest();
// 处理GET请求的参数拼接
let requestUrl = url;
if (method.toUpperCase() === 'GET' && data) {
const params = new URLSearchParams(data).toString();
requestUrl = `${url}?${params}`;
}
// 初始化请求
xhr.open(method, requestUrl, true);
// 设置响应类型
xhr.responseType = responseType;
// 设置超时时间
if (timeout > 0) {
xhr.timeout = timeout;
}
// 设置请求头
Object.keys(headers).forEach(key => {
xhr.setRequestHeader(key, headers[key]);
});
// 如果是POST请求且没有设置Content-Type,默认设置为json
if (method.toUpperCase() === 'POST' && !headers['Content-Type']) {
xhr.setRequestHeader('Content-Type', 'application/json;charset=UTF-8');
}
// 监听请求成功
xhr.onload = function() {
if (xhr.status >= 200 && xhr.status < 300) {
resolve({
data: xhr.response,
status: xhr.status,
statusText: xhr.statusText,
headers: xhr.getAllResponseHeaders()
});
} else {
reject(new Error(`请求失败: ${xhr.status} ${xhr.statusText}`));
}
};
// 监听请求错误
xhr.onerror = function() {
reject(new Error('网络请求失败'));
};
// 监听超时
xhr.ontimeout = function() {
reject(new Error('请求超时'));
};
// 监听请求中止
xhr.onabort = function() {
reject(new Error('请求已中止'));
};
// 发送请求
if (method.toUpperCase() === 'POST' && data) {
// POST请求发送JSON数据
xhr.send(JSON.stringify(data));
} else {
xhr.send();
}
});
}
使用示例
// 示例1: GET请求
ajax({
url: 'https://api.example.com/users',
method: 'GET',
data: { page: 1, limit: 10 }
})
.then(response => {
console.log('请求成功:', response.data);
})
.catch(error => {
console.error('请求失败:', error.message);
});
// 示例2: POST请求
ajax({
url: 'https://api.example.com/login',
method: 'POST',
data: {
username: 'admin',
password: '123456'
},
headers: {
'Authorization': 'Bearer token123'
},
timeout: 5000
})
.then(response => {
console.log('登录成功:', response.data);
})
.catch(error => {
console.error('登录失败:', error.message);
});
// 示例3: 使用async/await
async function fetchUserData() {
try {
const response = await ajax({
url: 'https://api.example.com/user/1',
method: 'GET',
responseType: 'json'
});
console.log('用户数据:', response.data);
} catch (error) {
console.error('获取失败:', error.message);
}
}
fetchUserData();
关键点
-
Promise封装:使用Promise包装XMLHttpRequest,使其支持现代异步编程风格,便于使用async/await
-
参数处理:GET请求需要将data对象转换为URL查询参数,POST请求需要将data转换为JSON字符串
-
请求初始化:使用
xhr.open(method, url, true)初始化请求,第三个参数true表示异步请求 -
状态监听:需要监听多个事件(onload、onerror、ontimeout、onabort)来完整处理各种情况
-
状态码判断:HTTP状态码在200-299之间才算成功,其他状态码应该reject
-
请求头设置:必须在
open()之后、send()之前调用setRequestHeader()设置请求头 -
响应类型:通过
responseType属性设置响应数据类型(json、text、blob等),自动解析响应数据 -
超时处理:设置
timeout属性并监听ontimeout事件,避免请求长时间挂起
目录