为何改编MDN简单AJAX示例的代码出现回调重复触发问题?
问题的根源其实出在XMLHttpRequest的事件机制上
你遇到的这个多次触发的问题,根本不是点击事件触发了多次——毕竟你已经加了{once: true}确保点击只会触发一次getData。真正的原因是你对onreadystatechange事件的理解有误:
这个事件会在XMLHttpRequest对象的readyState属性发生任何变化时触发,而不是只在请求完成时触发。XMLHttpRequest一共有5种readyState状态:
- 0: UNSENT(对象已创建但还没调用open)
- 1: OPENED(open方法已调用)
- 2: HEADERS_RECEIVED(send方法已调用,响应头已返回)
- 3: LOADING(响应体正在下载中)
- 4: DONE(请求彻底完成)
你的handleData函数没有做任何状态判断,所以每一次readyState变化,它都会被执行一次。比如正常情况下,一个AJAX请求会依次触发readyState从1到4的变化,也就是会调用4次handleData——这也解释了为什么你加了循环后会看到4组输出。而你之前看到的两次输出,可能是某些状态在你的测试环境中没有触发(比如有些浏览器可能会合并部分状态变化),但本质还是同一个问题。
再看看你参考的MDN示例,人家的alertContents函数开头就加了if (httpRequest.readyState === XMLHttpRequest.DONE)的判断,只有当请求彻底完成时才执行后续逻辑,所以只会触发一次。
解决办法
你有两种选择来修复这个问题:
- 照搬MDN的思路,在
handleData里先判断readyState是否为DONE:
function handleData() { if (httpRequest.readyState === XMLHttpRequest.DONE) { console.log("Handling data"); // 这里再处理你的响应数据 } }
- 改用
onload事件代替onreadystatechange,这个事件只会在请求成功完成(readyState=4且status=200)时触发:
// 把原来的 httpRequest.onreadystatechange = handleData; 改成 httpRequest.onload = handleData; function handleData() { console.log("Handling data"); // 直接处理响应即可 }
这样就能确保handleData只会被执行一次啦。
内容的提问来源于stack exchange,提问作者JonahHuron
相关产品推荐
相关产品推荐

