自己动手实现一个ajax?

news/2024/7/19 14:57:06 标签: js, javascript, ajax

文章目录

  • XMLHttpRequest基本使用方法
  • 实现一个简单的ajax
    • 思路
    • 实现
      • 浏览器兼容判断
      • 格式化请求参数
      • myAjax封装
    • 完整代码(含实验)
    • 结果
  • 结语
  • 参考资料

XMLHttpRequest基本使用方法

都说js是单线程的,那js是怎么实现异步请求的呢?

其实仔细了解了js的运行机制后,才发现,js会专门使用一个子线程来处理请求操作(XMLHttpRequest),只是因为这些线程都是主线程开出来的,是完全听命于主线程的,所以才这么认为js是单线程的(单主线程)。

img

1、readyState:返回当前文档的载入状态

0:(未初始化)还没有调用send()方法

1:(载入)已调用send()方法,正在发送请求

2:(载入完成)send()方法执行完成,已经接收到全部响应内容

3:(交互)正在解析响应内容

4:(完成)响应内容解析完成,可以在客户端调用了

在通过Ajax向服务器发送请求的过程中,XMLHttpRequest对象的状态会发生多次变化。由于 readystatechange 事件是在 xhr 对象状态变化时触发(不单是在得到响应时),也就意味着这个事件会被触发多次。XMLHttpRequest的主要状态事件如下:

js">var xhr = new XMLHttpRequest()
xhr.open('GET', '/time')
xhr.onload = function () {
    // onload readyState => 4
    // 只在请求完成时触发
    console.log(this.readyState)
}
xhr.onprogress = function () {
    // onprogress readyState => 3
    // 只在请求进行中触发
    console.log(this.readyState)
}
xhr.onloadstart = function () {
    // onloadstart readyState => 1
    // 开始发送请求的时候触发
    console.log(this.readyState)
}
xhr.onloadend = function () {
    // onloadend readyState => 4
    // 请求响应过程结束的时候触发
    console.log(this.readyState)
}

xhr.onreadystatechange = function () {
    // 请求响应状态每次改变都会触发  
}
xhr.send(null)

2、status:HTTP状态码

1XX:信息性状态码 ,表示接收的请求正在处理

2XX:成功状态码 , 表示请求正常处理

3XX:重定向状态码 ,表示需要附加操作来完成请求

4XX:客户端错误状态 ,表示服务器无法处理请求

5XX:服务器错误状态 ,表示服务器处理请求出错

3、 get和post

GET请求的参数需要放在queryString中

POST的请求数据一般是放在formData中(form表单),当然,还有其他的方式,比如payload中的json等

4、同步请求和异步请求

当async属性的值为false时是同步的,Ajax请求将整个浏览器锁死,只有ajax请求返回结果后,才执行ajax后面的alert语句。

当async属性的值为true时是异步的,即不会等待ajax请求返回的结果,会直接执行ajax后面的alert语句。

ajax_93">实现一个简单的ajax

  • 支持get、post两种请求方式

  • 支持同步、异步

  • 支持自定义传data的格式(get默认为queryString、post默认为formData)

  • 支持超时机制

  • 兼容ie6等早期版本

其中一个ajax的使用示例如下:

json">$.ajax({
    type: "GET",
    url: "www.baidu.com/s",
    data: { wd: "test" },
    contentType: "application/json;charset=utf-8",
    dataType: "json",  // 传回来的数据格式
    success: function (data) {
        // 更新视图
        // 关闭加载动画等操作
    }
});

contentType: 告诉服务器,我要发什么类型的数据

dataType:告诉服务器,我要想什么类型的数据,如果没有指定,那么会自动推断是返回 XML,还是JSON,还是script,还是String。

思路

首先用伪码来描述一下大致逻辑

js">// TODO: 将字典(obj键值对)转化为拼接queryString的函数方法。
function formatParams(data) {
   ...
   return formatParams
}


// TODO: 创建一个兼容性高的xhr
function createXMLHttpRequest(){
    ...
    return xhr
}

// 根据options进行同步或异步请求
function myAjax(options){
    读取options配置
   
    xhr = createXMLHttpRequest()
    
    // 发送请求部分
    if(get请求){
        // 这里的options.async为true或false  true为异步 false为同步
        xhr.open("GET", options.url + "?" + formatParams(options.data), options.async);
    }else if(是post请求){
        xhr.open("POST", options.url, options.async);
        
        contentType = options.contentType || "application/x-www-form-urlencoded"
        xhr.setRequestHeader("Content-type", contentType);
        xhr.send(params);
    }
    
    // 设置超时放弃请求机制
    setTimeout(function () {
        if(xhr状态未变成完成请求状态){
            // 中断请求
            xhr.abort();
        }
    }, options.timeout)
    
    // 请求完成后执行的操作 执行用户传入的success  fail钩子函数
     xhr.onreadystatechange = function () {
            if (xhr状态变成完成了请求状态) {
                if (状态码成功) {
                	执行成功的钩子函数
                } else {
                    执行失败的钩子函数
                }
            }

        }
}

实现

浏览器兼容判断

js">function createXMLHttpRequest() {
        var xhr;
        // 适用于大多数浏览器,以及IE7和IE更高版本
        try{
            xhr = new XMLHttpRequest();
        } catch (e) {
            // 适用于IE6
            try {
                xhr = new ActiveXObject("Msxml2.XMLHTTP");
            } catch (e) {
                // 适用于IE5.5,以及IE更早版本
                try{
                    xhr = new ActiveXObject("Microsoft.XMLHTTP");
                } catch (e){}
            }
        }            
        return xhr;
    }

格式化请求参数

js">// TODO: 格式化请求参数
function formatParams(data) {
    var arr = [];
    for (var key in data) {
        arr.push(encodeURIComponent(key) + "=" + encodeURIComponent(data[key]));
    }
    return arr.join("&");
}

img

myAjax封装

js">function myAjax(options) {
    //调用函数时如果options没有指定,就给它赋值{},一个空的Object
    options = options || {};

    // 请求格式GET、POST,默认为GET
    options.type = (options.type || "GET").toUpperCase();

    //响应数据格式,默认json
    options.dataType = options.dataType || "json";

    //Post请求时data的格式,默认为formData
    options.contentType = options.contentType || "application/x-www-form-urlencoded";

    // 同步请求还是异步请求  true为异步请求async 默认true
    options.async = options.async || true;

    // 默认超时时间 3分钟
    options.timeout = options.timeout || (1000 * 60 * 3)

    // 。。。。其它配置项暂时不扩展

    var xhr;

    xhr = new XMLHttpRequest();



    //启动并发送一个请求
    if (options.type == "GET") {
        xhr.open("GET", options.url + "?" + formatParams(options.data), options.async);
        xhr.send(null);
    } else if (options.type == "POST") {
        xhr.open("POST", options.url, options.async);

        // 设置请求头
        xhr.setRequestHeader("Content-Type", options.contentType);

        // 判断请求体
        if (options.contentType == "application/x-www-form-urlencoded") {
            xhr.send(formatParams(options.data));
        } else {
            xhr.send(options.data);
        }
    }

    //  设置有效时间
    setTimeout(function () {
        if (xhr.readySate != 4) {
            xhr.abort();
        }
    }, options.timeout)

    // 接收
    // options.success成功之后的回调函数  options.error失败后的回调函数    
    //xhr.responseText,xhr.responseXML  获得字符串形式的响应数据或者XML形式的响应数据
    xhr.onreadystatechange = function () {
        if (xhr.readyState == 4) {
            var status = xhr.status;
            if (status >= 200 && status < 300 || status == 304) {
                options.success && options.success(xhr.responseText, xhr.responseXML);
            } else {
                options.error && options.error(status);
            }
        }
    }
}



完整代码(含实验)

js"><!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <title></title>
    <style>
        main {
            width: 900px;
            height: 900px;
            border: 1px red solid;
            margin: 0 auto;
            position: relative
        }

        main div {
            width: 200px;
            height: 200px;
            border: 1px red solid;
            position: absolute;
            left: 0;
            right: 0;
            top: 0;
            bottom: 0;
            margin-left: auto;
            margin-right: auto;
            margin-top: auto;
            margin-bottom: auto;
        }
    </style>
</head>

<body>
    <main>
        <div>
            <button id="id_button_01">点我发送请求(jquery封装的ajax)</button>
            <button id="id_button_02">点我发送请求(自己封装的ajax)</button>

        </div>
        <span>发起请求次数(jquery封装的ajax)<p id="id_text_01"></p></span>
        <span>完成请求次数(jquery封装的ajax)<p id="id_text_02"></p></span>
        <span>发起请求次数(自己封装的ajax)<p id="id_text_03"></p></span>
        <span>完成请求次数(自己封装的ajax)<p id="id_text_04"></p></span>
    </main>
</body>
<script src="https://lib.sinaapp.com/js/jquery/2.0.2/jquery-2.0.2.min.js"></script>
<script>
    var counter01 = counter02 = counter03 = counter04 = 0;
    var eText01 = document.getElementById("id_text_01");
    var eText02 = document.getElementById("id_text_02");
    var eText03 = document.getElementById("id_text_03");
    var eText04 = document.getElementById("id_text_04");

    var eButton01 = document.getElementById("id_button_01");
    var eButton02 = document.getElementById("id_button_02");



    //TODO: 格式化请求参数
    function formatParams(data) {
        var arr = [];
        for (var key in data) {
            arr.push(encodeURIComponent(key) + "=" + encodeURIComponent(data[key]));
        }
        return arr.join("&");
    }

    // TODO: 创建一个兼容性高的xhr
    function createXMLHttpRequest() {
        var xhr;
        // 适用于大多数浏览器,以及IE7和IE更高版本
        try {
            xhr = new XMLHttpRequest();
        } catch (e) {
            // 适用于IE6
            try {
                xhr = new ActiveXObject("Msxml2.XMLHTTP");
            } catch (e) {
                // 适用于IE5.5,以及IE更早版本
                try {
                    xhr = new ActiveXObject("Microsoft.XMLHTTP");
                } catch (e) { }
            }
        }
        return xhr;
    }


    function myAjax(options) {
        //调用函数时如果options没有指定,就给它赋值{},一个空的Object
        options = options || {};

        // 请求格式GET、POST,默认为GET
        options.type = (options.type || "GET").toUpperCase();

        //响应数据格式,默认json
        options.dataType = options.dataType || "json";

        //Post请求时data的格式,默认为formData
        options.contentType = options.contentType || "application/x-www-form-urlencoded";

        // 同步请求还是异步请求  true为异步请求async 默认true
        options.async = options.async || true;

        // 默认超时时间 3分钟
        options.timeout = options.timeout || (1000 * 60 * 3)

        // 。。。。其它配置项暂时不扩展

        var xhr;

        xhr = new XMLHttpRequest();



        //启动并发送一个请求
        if (options.type == "GET") {
            xhr.open("GET", options.url + "?" + formatParams(options.data), options.async);
            xhr.send(null);
        } else if (options.type == "POST") {
            xhr.open("POST", options.url, options.async);

            // 设置请求头
            xhr.setRequestHeader("Content-Type", options.contentType);

            // 判断请求体
            if (options.contentType == "application/x-www-form-urlencoded") {
                xhr.send(formatParams(options.data));
            } else {
                xhr.send(options.data);
            }
        }

        //  设置有效时间
        setTimeout(function () {
            if (xhr.readySate != 4) {
                xhr.abort();
            }
        }, options.timeout)

        // 接收
        // options.success成功之后的回调函数  options.error失败后的回调函数    
        //xhr.responseText,xhr.responseXML  获得字符串形式的响应数据或者XML形式的响应数据
        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4) {
                var status = xhr.status;
                if (status >= 200 && status < 300 || status == 304) {
                    options.success && options.success(xhr.responseText, xhr.responseXML);
                } else {
                    options.error && options.error(status);
                }
            }
        }
    }


    eButton01.onclick = function () {
        console.log("发起了一个请求");
        counter01++;
        eText01.innerHTML = counter01;

        $.ajax({
            url: "test.txt",
            type: 'get',
            data: {
                wd: '特朗普',
            },
            dataType: 'html',
            timeout: 50000,
            success: function (res) {
                console.log("发送请求成功");
                console.log(res);
                counter02++;
                eText02.innerHTML = counter02;
            },
            error: function (e) {
                //异常处理
                console.log(e);
            }
        })

    }
    eButton02.onclick = function () {
        console.log("发起了一个请求");
        counter03++;
        eText03.innerHTML = counter03;
        myAjax({
            url: "test.txt",
            type: "get",
            data: {
                wd: '特朗普',
            },
            dataType: 'html',
            timeout: 50000,
            success: function (res) {
                console.log("发送请求成功");
                console.log(res);
                counter04++;
                eText04.innerHTML = counter04;
            },
            error: function (e) {
                //异常处理
                console.log(e);
            }
        })
    }

</script>

</html>

结果

img

img

img

可以看到实现得很成功。

结语

之后我将继续深入研究用原生js实现promise。

参考资料

https://www.w3school.com.cn/ajax/ajax_xmlhttprequest_create.asp w3school xmlhttprequest

https://www.w3school.com.cn/jquery/ajax_ajax.asp jQuery ajax - ajax() 方法

https://www.cnblogs.com/qing-5/p/11368009.html [原生js实现ajax封装]

https://www.cnblogs.com/colima/p/5339227.html [原生js实现Ajax]

https://www.jb51.net/article/119605.htm 深入讲解xhr(XMLHttpRequest)/jsonp请求之abort

《JavaScript权威指南》


http://www.niftyadmin.cn/n/555377.html

相关文章

MySQL 数据库 -- 数据操作

数据的增删改 一 介绍 MySQL数据操作&#xff1a; DML 在MySQL管理软件中&#xff0c;可以通过SQL语句中的DML语言来实现数据的操作&#xff0c;包括 使用INSERT实现数据的插入UPDATE实现数据的更新使用DELETE实现数据的删除使用SELECT查询数据以及。 二 插入数据INSERT 1. 插…

华工简述微型计算机系统的组成,华工 计算机组成原理随堂.doc

华工 计算机组成原理随堂第一章 计算机系统概论1.1 计算机的分类和应用本次练习有4题&#xff0c;你已做4题&#xff0c;已提交4题&#xff0c;其中答对题。 当前页有4题&#xff0c;你已做4题&#xff0c;已提交4题&#xff0c;其中答对题。1.? 大约(??)年&#xff0c;计算…

剑指offer系列之青蛙上台阶(动态规划入门级)

前言 阅读完本文&#xff0c;可以去leetcode把这题秒了。 https://leetcode-cn.com/problems/qing-wa-tiao-tai-jie-wen-ti-lcof/submissions/ 问题描述&#xff1a;一只青蛙一次可以跳上1级台阶&#xff0c;也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。 思…

02-django查询

目录&#xff08;一&#xff09;查询1 、基本查询&#xff08;等于、大于、包含字符、日期、字段比较、逻辑&#xff09;2 、关联查询&#xff08;即为join查询&#xff09;&#xff08;一对多、多对多、一对一&#xff09;3 、聚合查询&#xff08;统计查询&#xff09;&#…

css html 45度角箭头,使用css实现任意大小、任意方向和任意角度的箭头示例

网页开发中&#xff0c;经常会使用到 下拉箭头&#xff0c;右侧箭头这样的箭头。 一般用css来实现&#xff1a;{display: inline-block;margin: 72px;border-top: 24px solid;border-right: 24px solid;width: 120px;height: 120px;transform: rotate(45deg);}因为这是利用div的…

(Leetcode)no.62 不同路径(动态规划做法)

文章目录题目思路&#xff08;一&#xff09;定义数组元素的含义&#xff08;二&#xff09;找出关系数组元素间的关系式&#xff08;三&#xff09;找出初始值代码其它解法题目 题目&#xff1a;这里 思路 &#xff08;一&#xff09;定义数组元素的含义 定义 dp[i] [j]的含…

Linux基础(一)

第一单元 Linux的安装及相关配置 1、计算机操作系统简介 操作系统是什么: 操作系统是一个用来协调、管理和控制计算机硬件和软件资源的系统程序&#xff0c;它位于硬件和应用程序之间。 操作系统的内核是什么: 操作系统的内核是一个管理和控制程序&#xff0c;负责管理计算机的…

奇葩问题 eclipse下 maven项目 java Resource报个小红叉,然而里面却没有小红叉

之前没注意&#xff0c;不知是一开始就有还是这两天才有&#xff0c;说下解决方案&#xff1a; 右击项目“Properties”&#xff0c;在弹出的“Properties”的左侧边框&#xff0c;单击“Project Facets”&#xff0c;打开“Project Facets”页面&#xff0c; 在页面中“Java”…