如何获取XMLHttpRequest请求状态布尔值并传递给validateForm校验函数
相似问题
以下是我已经尝试套用在自身场景中,但没能解决当前问题的相似内容。如果您也遇到同类问题,可参考这些资料:
- 如何从异步调用中返回响应
- 如何从AJAX的onreadystatechange事件中获取返回值
- 如何通过回调从onreadystatechange中获取返回值
背景
我有一个包含三个输入项(用户名、密码、确认密码)的表单,需要通过Javascript做规则校验(比如密码最小长度等),因此编写了validateForm函数处理校验逻辑。
其中一项校验规则是用户名不能已存在,因此我编写了userExists函数(见下文代码)供validateForm调用。userExists单独运行功能正常,控制台可以正确打印"Exists"或"Does not exist"。
遇到的问题
但我现在无法在validateForm函数中获取到userExists返回的布尔值(true/false),梳理出两个难点:
- 无法从onreadystatechange属性中取出返回值
- 异步函数问题:我尝试通过callback解决,但无法让validateForm等待userExists执行完成后再继续后续逻辑
我的问题
请问我要如何将用户名是否存在的信息从userExists传递到validateForm中?我刚接触Javascript,对相关特性还不太熟悉,这个问题已经卡了两天了,不清楚是结构问题还是语法问题,非常希望能得到大家的帮助。
代码
var val; var exists; var performSomeAction = function (exists) { val = exists; } function validateForm() { const name = document.getElementById("username").value; //checking some inputs //check if the username is already in use console.log(nameExists); else if (nameExists) { alert("The name is already in use"); name = ""; return false; } //validating more inputs } function userExists(callback) { var xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = function () { if (xmlhttp.readyState == 4 && xmlhttp.status == 204) { console.log("Exists"); callback(true); } else if (xmlhttp.readyState == 4 && xmlhttp.status == 404) { console.log("Does not exist"); callback(false); } }; xmlhttp.open("GET", "#aLinkThatIsNotTheIssue", true); xmlhttp.send(""); }
运行结果
当前控制台输出内容如下:
undefined Exists
问题原因
你遇到的是JS异步执行的典型顺序问题:XMLHttpRequest是异步请求,你调用userExists之后,JS不会等待请求返回结果,会直接往下执行validateForm里剩下的代码,所以你在请求还没完成的时候读取nameExists自然是undefined,等请求完成触发回调的时候,validateForm早就执行完了。
解决方案
这里给两种常用的实现方式:
方案1:用回调函数实现(基于你现有的callback结构调整即可)
不需要试图从userExists里直接拿返回值,把需要用到nameExists的后续逻辑都放到回调函数里就行:
function validateForm() { const nameInput = document.getElementById("username"); const name = nameInput.value; // 先做不需要等异步结果的同步校验,比如非空、长度校验等 if (name.trim() === '') { alert('用户名不能为空'); return false; } // 其他同步校验逻辑(比如密码长度、两次密码一致等)可以先写在这里 // 调用用户名存在校验,后续逻辑全部放到回调里执行 userExists(function(nameExists) { console.log(nameExists); if (nameExists) { alert("该用户名已被使用"); nameInput.value = ""; return false; } // 剩下的所有需要依赖nameExists的校验、以及校验通过后的表单提交逻辑,都写在这里 // 示例:校验全部通过后提交表单 document.getElementById('你的表单ID').submit(); }); // 注意:此处不能写任何需要依赖nameExists的逻辑,执行到这里的时候请求还没返回,拿不到值 }
方案2:用Promise + async/await实现(代码逻辑更线性,更容易理解)
先把userExists改造成返回Promise的形式,再用await等待异步结果返回再继续执行后续逻辑:
// 改造后的userExists function userExists() { return new Promise((resolve) => { var xmlhttp = new XMLHttpRequest(); xmlhttp.onreadystatechange = function () { if (xmlhttp.readyState == 4 && xmlhttp.status == 204) { console.log("Exists"); resolve(true); } else if (xmlhttp.readyState == 4 && xmlhttp.status == 404) { console.log("Does not exist"); resolve(false); } }; xmlhttp.open("GET", "#aLinkThatIsNotTheIssue", true); xmlhttp.send(""); }) } // 改造为async函数的validateForm async function validateForm() { const nameInput = document.getElementById("username"); const name = nameInput.value; // 同步校验逻辑 if (name.trim() === '') { alert('用户名不能为空'); return false; } // 其他同步校验... // 等待异步请求返回结果,再往下执行 const nameExists = await userExists(); console.log(nameExists); if (nameExists) { alert("该用户名已被使用"); nameInput.value = ""; return false; } // 剩余校验逻辑、表单提交逻辑 if (/* 所有校验都通过 */) { document.getElementById('你的表单ID').submit(); } }
如果你的表单是通过原生submit事件触发校验,记得先阻止默认提交行为,避免校验未完成表单就被提交:
document.getElementById('你的表单ID').addEventListener('submit', async function(e) { e.preventDefault(); // 先阻止默认提交 await validateForm(); })
内容的提问来源于stack exchange,提问作者butterfly

