JavaScript中XMLHttpRequest请求if/else分支同时执行问题排查
问题现象
在JavaScript中使用if (xhr.readyState == 4 && xhr.status == 200)判断XMLHttpRequest请求状态时出现逻辑异常:开发PHP MVC项目个人资料编辑页前端交互时,if、else分支代码均被执行——个人资料编辑操作实际成功,接口正常返回JSON数据,也能正常跳转至Profile页面,但else分支定义的错误提示“Sorry, this content isn't available right now”仍会触发展示。代码保存为本地文件运行时功能完全正常。
核心实现代码
document.querySelector("#Profile_Save").addEventListener("click", () => { if (document.querySelector("#Profile_Edit_Email").value.match(/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*$/)) { document.querySelector("#Profile_Edit_Msg").classList.remove("active_success"); document.querySelector("#Profile_Edit_Msg").classList.remove("active_denger"); document.querySelector("#Profile_Save").innerHTML = "Loading..."; document.querySelector("#Profile_Save").classList.remove("active"); document.querySelector("#Profile_Save").disabled = true; document.querySelector("#Profile_Edit_F_Name").disabled = true; document.querySelector("#Profile_Edit_L_Name").disabled = true; document.querySelector("#Profile_Edit_Email").disabled = true; var f_name = document.querySelector("#Profile_Edit_F_Name").value, l_name = document.querySelector("#Profile_Edit_L_Name").value, email = document.querySelector("#Profile_Edit_Email").value; var xhr = new XMLHttpRequest(); xhr.open("POST", "Api/ProfileEdit", true); xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xhr.onreadystatechange = function() { if (xhr.readyState == 4 && xhr.status == 200) { // 成功分支实际执行 var json = JSON.parse(xhr.responseText); if (json.Status == "Ok") { window.location.href = "Profile"; // 跳转逻辑正常生效 } else { document.querySelector("#Profile_Edit_Msg").classList.remove("active_success"); document.querySelector("#Profile_Edit_Msg").classList.add("active_denger"); document.querySelector("#Profile_Edit_Msg").innerHTML = json.Message; } } else { // 错误分支也被执行 document.querySelector("#Profile_Edit_Msg").classList.add("active_denger"); document.querySelector("#Profile_Edit_Msg").innerHTML = "Sorry, this content isn't available right now"; // 错误提示弹出 } } xhr.send("F_Name=" + f_name + "&L_Name=" + l_name + "&Email=" + email); // 发送请求后立刻重置按钮状态 document.querySelector("#Profile_Save").innerHTML = "Register"; document.querySelector("#Profile_Save").classList.add("active"); document.querySelector("#Profile_Save").disabled = false; document.querySelector("#Profile_Edit_F_Name").disabled = false; document.querySelector("#Profile_Edit_L_Name").disabled = false; document.querySelector("#Profile_Edit_Email").disabled = false; } else { document.querySelector("#Profile_Edit_Msg").classList.add("active_denger"); document.querySelector("#Profile_Edit_Msg").innerHTML = "Invalid Email Address!"; } });
接口返回内容
{"Status":"Ok","Message":"Profile Edit Successfully!"}
实际运行效果
跳转个人主页前,会先展示错误提示,截图如下:
故障原因
问题出在onreadystatechange事件的判断逻辑上:
- XMLHttpRequest的
onreadystatechange事件会在readyState状态变化时触发,整个请求生命周期一共会触发5次,对应readyState的5个值:- 0:请求未初始化
- 1:已建立服务器连接
- 2:请求已被服务器接收
- 3:正在处理请求
- 4:请求已完成,响应已就绪
- 现有逻辑判断为「如果不是readyState=4且status=200,就直接走else弹错误提示」,意味着readyState为0/1/2/3的中间阶段,每次事件触发都会执行错误提示逻辑,这就是错误提示提前弹出的根本原因。
- 另外代码还存在逻辑瑕疵:
xhr.send()发送请求后立刻重置了按钮、恢复了表单编辑状态,没有等请求完成,Loading状态根本不会正常生效。
修复方案
调整事件内判断逻辑,仅在readyState=4(请求完成)时处理后续逻辑:请求完成后先统一重置按钮和表单状态,再根据HTTP状态码判断是走成功解析分支还是网络错误分支,请求中间阶段不做任何错误提示。
修正后的核心代码如下:
xhr.onreadystatechange = function() { // 仅在请求完全完成时处理逻辑 if (xhr.readyState === 4) { // 统一重置按钮和表单状态,无论请求成功失败都要恢复可操作状态 document.querySelector("#Profile_Save").innerHTML = "Register"; document.querySelector("#Profile_Save").classList.add("active"); document.querySelector("#Profile_Save").disabled = false; document.querySelector("#Profile_Edit_F_Name").disabled = false; document.querySelector("#Profile_Edit_L_Name").disabled = false; document.querySelector("#Profile_Edit_Email").disabled = false; if (xhr.status === 200) { const json = JSON.parse(xhr.responseText); if (json.Status === "Ok") { window.location.href = "Profile"; } else { document.querySelector("#Profile_Edit_Msg").classList.remove("active_success"); document.querySelector("#Profile_Edit_Msg").classList.add("active_denger"); document.querySelector("#Profile_Edit_Msg").innerHTML = json.Message; } } else { // 只有请求完成但HTTP状态异常时才提示网络错误 document.querySelector("#Profile_Edit_Msg").classList.add("active_denger"); document.querySelector("#Profile_Edit_Msg").innerHTML = "Sorry, this content isn't available right now"; } } } xhr.send("F_Name=" + encodeURIComponent(f_name) + "&L_Name=" + encodeURIComponent(l_name) + "&Email=" + encodeURIComponent(email)); // 删除原来xhr.send()后面写的重置按钮、恢复表单的代码,这部分逻辑已经移到请求完成的回调里
额外优化点:发送POST参数时用encodeURIComponent转义特殊字符,避免参数里含特殊符号时接口解析异常;另外代码里写的样式类名active_denger是拼写错误,正确应为active_danger,可自行修正。
内容的提问来源于stack exchange,提问作者Uttam Nath
相关产品推荐
相关产品推荐

