欢迎光临白事网
详情描述
Ajax 异步请求方案详解

Ajax(Asynchronous JavaScript and XML)是一种无需重新加载整个网页的情况下,能够更新部分网页的技术。下面我将详细讲解使用原生XMLHttpRequest对象实现Ajax请求的方案。

一、XMLHttpRequest 对象基础

1. 创建XMLHttpRequest对象

// 兼容性处理
function createXHR() {
    if (typeof XMLHttpRequest !== 'undefined') {
        return new XMLHttpRequest();
    } else if (typeof ActiveXObject !== 'undefined') {
        // 兼容旧版IE
        var versions = [
            'MSXML2.XMLHttp.6.0',
            'MSXML2.XMLHttp.3.0',
            'MSXML2.XMLHttp'
        ];
        for (var i = 0; i < versions.length; i++) {
            try {
                return new ActiveXObject(versions[i]);
            } catch (e) {
                // 继续尝试下一个版本
            }
        }
    }
    throw new Error('浏览器不支持XMLHttpRequest');
}

// 使用
const xhr = createXHR();

2. 完整的请求流程

function ajaxRequest(config) {
    const {
        method = 'GET',
        url,
        data = null,
        headers = {},
        timeout = 0,
        responseType = 'text',
        onSuccess,
        onError,
        onTimeout,
        onProgress
    } = config;

    // 创建XHR对象
    const xhr = new XMLHttpRequest();

    // 设置响应类型
    xhr.responseType = responseType;

    // 设置超时时间
    xhr.timeout = timeout;

    // 监听状态变化
    xhr.onreadystatechange = function() {
        if (xhr.readyState === XMLHttpRequest.DONE) {
            if (xhr.status >= 200 && xhr.status < 300) {
                // 请求成功
                if (onSuccess) {
                    onSuccess({
                        data: xhr.response,
                        status: xhr.status,
                        statusText: xhr.statusText,
                        headers: xhr.getAllResponseHeaders()
                    });
                }
            } else {
                // 请求失败
                if (onError) {
                    onError({
                        status: xhr.status,
                        statusText: xhr.statusText,
                        error: xhr.response
                    });
                }
            }
        }
    };

    // 监听超时
    xhr.ontimeout = function() {
        if (onTimeout) onTimeout();
    };

    // 监听进度
    if (onProgress) {
        xhr.onprogress = onProgress;
        if (xhr.upload && method !== 'GET') {
            xhr.upload.onprogress = onProgress;
        }
    }

    // 处理请求数据
    let requestData = data;
    let requestUrl = url;

    // 对于GET请求,将数据附加到URL
    if (method === 'GET' && data) {
        const queryString = typeof data === 'object' 
            ? Object.keys(data).map(key => 
                `${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`
            ).join('&')
            : data.toString();

        if (queryString) {
            requestUrl += (url.includes('?') ? '&' : '?') + queryString;
        }
        requestData = null;
    } else if (data && typeof data === 'object') {
        // 默认使用JSON格式
        if (!headers['Content-Type']) {
            headers['Content-Type'] = 'application/json;charset=UTF-8';
            requestData = JSON.stringify(data);
        }
    }

    // 打开连接
    xhr.open(method, requestUrl, true);

    // 设置请求头
    Object.keys(headers).forEach(key => {
        xhr.setRequestHeader(key, headers[key]);
    });

    // 发送请求
    xhr.send(requestData);

    return xhr;
}

二、实际应用示例

1. GET请求示例

// 获取用户信息
function getUser(userId) {
    ajaxRequest({
        method: 'GET',
        url: `https://api.example.com/users/${userId}`,
        onSuccess: function(response) {
            console.log('用户数据:', response.data);
            renderUserProfile(response.data);
        },
        onError: function(error) {
            console.error('获取用户失败:', error);
            showError('无法加载用户信息');
        }
    });
}

2. POST请求示例

// 提交表单数据
function submitForm(formData) {
    ajaxRequest({
        method: 'POST',
        url: 'https://api.example.com/submit',
        data: formData,
        headers: {
            'X-CSRF-Token': getCSRFToken() // 自定义请求头
        },
        onSuccess: function(response) {
            console.log('提交成功:', response.data);
            showSuccessMessage('操作成功完成');
        },
        onError: function(error) {
            console.error('提交失败:', error);
            if (error.status === 401) {
                redirectToLogin();
            } else {
                showError('提交失败,请重试');
            }
        }
    });
}

3. 上传文件

// 文件上传
function uploadFile(file, onProgressCallback) {
    const formData = new FormData();
    formData.append('file', file);
    formData.append('description', '用户上传的文件');

    ajaxRequest({
        method: 'POST',
        url: 'https://api.example.com/upload',
        data: formData,
        // FormData会自动设置Content-Type为multipart/form-data
        onProgress: function(event) {
            if (event.lengthComputable) {
                const percentComplete = Math.round((event.loaded / event.total) * 100);
                onProgressCallback(percentComplete);
            }
        },
        onSuccess: function(response) {
            console.log('上传成功:', response.data);
            updateFileList(response.data.fileUrl);
        },
        onError: function(error) {
            console.error('上传失败:', error);
            showError('文件上传失败');
        }
    });
}

三、高级特性与最佳实践

1. 请求取消

class RequestManager {
    constructor() {
        this.pendingRequests = new Map();
    }

    // 发送请求并存储引用
    sendRequest(requestId, config) {
        const xhr = ajaxRequest({
            ...config,
            onSuccess: (response) => {
                this.pendingRequests.delete(requestId);
                if (config.onSuccess) config.onSuccess(response);
            },
            onError: (error) => {
                this.pendingRequests.delete(requestId);
                if (config.onError) config.onError(error);
            }
        });

        this.pendingRequests.set(requestId, xhr);
        return requestId;
    }

    // 取消请求
    cancelRequest(requestId) {
        const xhr = this.pendingRequests.get(requestId);
        if (xhr) {
            xhr.abort();
            this.pendingRequests.delete(requestId);
            console.log(`请求 ${requestId} 已取消`);
        }
    }

    // 取消所有请求
    cancelAllRequests() {
        this.pendingRequests.forEach(xhr => xhr.abort());
        this.pendingRequests.clear();
    }
}

2. 请求拦截器

class AjaxInterceptor {
    constructor() {
        this.requestInterceptors = [];
        this.responseInterceptors = [];
    }

    // 添加请求拦截器
    addRequestInterceptor(interceptor) {
        this.requestInterceptors.push(interceptor);
    }

    // 添加响应拦截器
    addResponseInterceptor(interceptor) {
        this.responseInterceptors.push(interceptor);
    }

    // 发送请求(带拦截器)
    request(config) {
        // 执行请求拦截器
        let processedConfig = { ...config };
        this.requestInterceptors.forEach(interceptor => {
            processedConfig = interceptor(processedConfig) || processedConfig;
        });

        // 发送请求
        return new Promise((resolve, reject) => {
            ajaxRequest({
                ...processedConfig,
                onSuccess: (response) => {
                    // 执行响应拦截器
                    let processedResponse = response;
                    this.responseInterceptors.forEach(interceptor => {
                        processedResponse = interceptor(processedResponse) || processedResponse;
                    });
                    resolve(processedResponse);
                },
                onError: (error) => {
                    reject(error);
                }
            });
        });
    }
}

3. 自动重试机制

function ajaxWithRetry(config, maxRetries = 3, retryDelay = 1000) {
    let retries = 0;

    function attemptRequest() {
        return new Promise((resolve, reject) => {
            ajaxRequest({
                ...config,
                onSuccess: resolve,
                onError: (error) => {
                    retries++;
                    if (retries <= maxRetries && shouldRetry(error.status)) {
                        console.log(`请求失败,${retryDelay}ms后重试 (${retries}/${maxRetries})`);
                        setTimeout(() => {
                            attemptRequest().then(resolve).catch(reject);
                        }, retryDelay * retries); // 指数退避
                    } else {
                        reject(error);
                    }
                }
            });
        });
    }

    function shouldRetry(status) {
        // 对服务器错误和网络错误进行重试
        return status >= 500 || status === 0 || status === 408;
    }

    return attemptRequest();
}

四、兼容性与注意事项

1. 跨域请求

// CORS处理
function corsRequest() {
    ajaxRequest({
        method: 'GET',
        url: 'https://api.other-domain.com/data',
        // 如果需要凭证
        headers: {
            // CORS相关头部由浏览器自动处理
        },
        // 注意:跨域请求默认不发送cookie
        // 如果需要发送cookie,服务器需要设置Access-Control-Allow-Credentials: true
        // 且前端需要设置xhr.withCredentials = true
        beforeSend: (xhr) => {
            xhr.withCredentials = true;
        }
    });
}

2. 错误处理最佳实践

// 统一错误处理
function safeAjaxRequest(config) {
    return new Promise((resolve, reject) => {
        ajaxRequest({
            ...config,
            onSuccess: (response) => {
                // 检查业务逻辑错误
                if (response.data && response.data.code !== 0) {
                    handleBusinessError(response.data);
                    reject(new Error(response.data.message));
                } else {
                    resolve(response);
                }
            },
            onError: (error) => {
                // 统一错误处理
                const errorMessage = getErrorMessage(error.status);
                showNotification(errorMessage, 'error');
                logError(error);
                reject(error);
            }
        });
    });
}

function getErrorMessage(status) {
    const errorMessages = {
        400: '请求参数错误',
        401: '请先登录',
        403: '没有权限',
        404: '资源不存在',
        500: '服务器错误',
        502: '网关错误',
        503: '服务不可用',
        504: '网关超时',
        0: '网络连接失败'
    };
    return errorMessages[status] || `请求失败 (${status})`;
}

五、现代化替代方案

虽然XMLHttpRequest是Ajax的原始实现,但现在更推荐使用Fetch API:

// Fetch API 示例
async function fetchData() {
    try {
        const response = await fetch('https://api.example.com/data', {
            method: 'GET',
            headers: {
                'Content-Type': 'application/json'
            }
        });

        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }

        const data = await response.json();
        return data;
    } catch (error) {
        console.error('请求失败:', error);
        throw error;
    }
}

总结

Ajax通过XMLHttpRequest对象实现了异步通信,虽然现在有更现代的Fetch API,但理解XMLHttpRequest的工作原理仍然很重要,因为:

兼容性:XMLHttpRequest在几乎所有浏览器中都得到支持 控制力:提供更细粒度的控制(如进度监控) 历史代码:许多遗留系统仍在使用

建议在新项目中使用Fetch API或基于Promise的HTTP客户端(如Axios),但在需要更精细控制或兼容旧浏览器时,XMLHttpRequest仍然是可靠的选择。

相关帖子
linux系统监控工具小神器!超全面的Btop安装和使用指南
linux系统监控工具小神器!超全面的Btop安装和使用指南
荆州市白事殡葬一条龙服务-丧事一条龙服务,丧礼布置
荆州市白事殡葬一条龙服务-丧事一条龙服务,丧礼布置
未来社保全国统筹推进,2026年临时断缴的影响是否会减弱?
未来社保全国统筹推进,2026年临时断缴的影响是否会减弱?
年终奖发放时间有统一规定吗,最晚何时发放算合规?
年终奖发放时间有统一规定吗,最晚何时发放算合规?
2026年以旧换新政策在推动可持续发展方面扮演了什么角色?
2026年以旧换新政策在推动可持续发展方面扮演了什么角色?
hpsysdrv.exe是什么进程?hpsysdrv.exe是病毒吗?
hpsysdrv.exe是什么进程?hpsysdrv.exe是病毒吗?
汕尾市AI数字人制作小视频#企业网站建设公司,专业开发团队
汕尾市AI数字人制作小视频#企业网站建设公司,专业开发团队
湘潭市小视频制作与剪辑&商城网站定制,专业设计团队
湘潭市小视频制作与剪辑&商城网站定制,专业设计团队
coreldraw怎么勾画轮廓图?coreldraw勾画轮廓图方法
coreldraw怎么勾画轮廓图?coreldraw勾画轮廓图方法
Pandas索引器 loc 和 iloc 比较及代码示例
Pandas索引器 loc 和 iloc 比较及代码示例
MYSQL的日志文件详解
MYSQL的日志文件详解
益阳市殡葬一条龙公司电话|家庭白事服务,24小时服务热线
益阳市殡葬一条龙公司电话|家庭白事服务,24小时服务热线
2026年婚纱租赁店的卫生管理,是否有相关的行业规范或标准可以参考?
2026年婚纱租赁店的卫生管理,是否有相关的行业规范或标准可以参考?
面对一些生活必需品的价格波动,普通家庭可以采取哪些策略来平稳开支?
面对一些生活必需品的价格波动,普通家庭可以采取哪些策略来平稳开支?
手机号不再使用,但忘记绑定了哪些应用,有什么方法可以全面查询和解绑?
手机号不再使用,但忘记绑定了哪些应用,有什么方法可以全面查询和解绑?
对于网络上流传的历史类或社会类文章,普通读者可以从哪些角度评估其可信度?
对于网络上流传的历史类或社会类文章,普通读者可以从哪些角度评估其可信度?
Tomcat服务器日志超详细讲解
Tomcat服务器日志超详细讲解
AJAX POST数据中有特殊符号(转义字符)导致数据丢失的解决方法
AJAX POST数据中有特殊符号(转义字符)导致数据丢失的解决方法
明明知道熬夜不好,为什么就是控制不住自己?如何克服报复性熬夜?
明明知道熬夜不好,为什么就是控制不住自己?如何克服报复性熬夜?
不同城市间关于父母随迁落户的具体执行细则,主要差异体现在哪里?
不同城市间关于父母随迁落户的具体执行细则,主要差异体现在哪里?