解决原生JS XMLHttpRequest onreadystatechange回调执行两次的问题

作者:木棉时间:2026-10-26 11:03:48浏览:182

本文分析原生JS中XMLHttpRequest对象在IE等浏览器下onreadystatechange回调执行两次的原因。指出仅判断status==200会导致预检请求或状态变化时重复触发,通过增加xmlHttp.readyState == 4的判断条件,确保仅在数据完全解析后执行逻辑,从而解决重复执行问题。

原生js XMLHttpRequest请求中onreadystatechange回调执行两次的问题

近期开发一个需要兼容IE浏览器的页面,因此使用了原生的XMLHttpRequest进行网络请求。

首先展示导致问题的错误代码示例。

xmlHttp = new XMLHttpRequest();
        xmlHttp.open("post","https://baidu.com/mianxiang/baidu/biancheng");
        xmlHttp.setRequestHeader("Content-Type","application/json");
        xmlHttp.send(XXXXXXXXXX) ;
        xmlHttp.onreadystatechange = function () {
            if(this.status==200){
                console.log("responseText",this.responseText);
            }
        };

在上述代码中,每次请求控制台都会打印两次status等于200时的内容,意味着内部逻辑被执行了两次。经过大量搜索,未找到类似问题的具体解决方案。

xmlHttp = new XMLHttpRequest();
        xmlHttp.open("post","https://baidu.com/mianxiang/baidu/biancheng");
        xmlHttp.setRequestHeader("Content-Type","application/json");
        xmlHttp.send(XXXXXXXXXX) ;
        xmlHttp.onreadystatechange = function () {
            if(xmlHttp.readyState == 4 && this.status==200){
                console.log("responseText",this.responseText);
            }
        };

最终偶然发现正确代码中多了一行判断:xmlHttp.readyState == 4。

添加该判断后,请求只执行一次,问题得以解决。

分析原因可能是未添加readyState判断时,OPTIONS预检请求也会触发状态码200,导致执行两次。但疑惑点在于预检请求通常不返回数据,而此处两次打印均包含数据。

通过查阅资料并请教资深开发者,获得了相关知识。

核心知识点如下:

  • 创建XMLHttpRequest对象后、调用open()之前,readyState值为0;调用open()后变为1,此时onreadystatechange函数与open()几乎同时执行。
  • 在startHttpRequest函数中调用send方法后,readState值仍为1;调用send后应有2、3、4三个状态值,但仅在startHttpRequest函数中使用alert语句才能观察到这三个值的变化。
  • 原因在于当代码解析到send语句时,并未真正开始执行send操作。
  • 只有send真正执行后,才能在onreadystatechange函数中观察到状态值的变化。
  • readyState反映的是准备发送的状态,可类比为“人间大炮一级准备、二级准备、发射”的过程,而非请求发送本身。
  • 同时,xmlHttp并非持续监听服务器信息,而是在send时获取服务器返回的状态信息,仅一次;监听则是持续观察状态。

关于readyState不同状态的总结

(0) 未初始化

此阶段确认XMLHttpRequest对象是否已创建,并为调用open()方法进行未初始化作好准备。

值为0表示对象已经存在,否则浏览器会报错——对象不存在。

(1) 载入

此阶段对XMLHttpRequest对象进行初始化,即调用open()方法,根据参数(method, url, true)完成对象状态的设置。

并调用send()方法开始向服务端发送请求。值为1表示正在向服务端发送请求。

(2) 载入完成

此阶段接收服务器端的响应数据。但获得的还只是服务端响应的原始数据,并不能直接在客户端使用。

值为2表示已经接收完全部响应数据,并为下一阶段对数据解析作好准备。

(3) 交互

此阶段解析接收到的服务器端响应数据。

即根据服务器端响应头部返回的MIME类型把数据转换成能通过responseBody、responseText或responseXML属性存取的格式,为在客户端调用作好准备。

状态3表示正在解析数据。

(4) 完成

此阶段确认全部数据都已经解析为客户端可用的格式,解析已经完成。

值为4表示数据解析完毕,可以通过XMLHttpRequest对象的相应属性取得数据。

回顾之前为何执行两次onreadystatechange:因为当state每次变化的时候都会执行到onreadystatechange,其实是readyState每次变化都会有执行onreadystatechange。由于只判断了this.status == 200,所以当服务器响应后返回了200的状态码,才会执行console.log(),导致上面的执行两次的问题。

至此问题解决!

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持本站。

作者最新文章