jQuery表单验证:日期字段validate异步返回致提交逻辑错误
你遇到的核心问题是**#expiryDate字段的验证是异步执行的**,而你的代码是按同步逻辑编写的——在异步验证的回调函数修改formValid之前,判断是否提交的代码已经跑完了,自然会错误地执行$.post()。
从你的控制台日志就能清晰看到这个时序问题:
1526495506879 initial value: true
1526495506881 after expiryDate validate: true
1526495506882 in documentName validate: true
1526495506883 after documentName validate: true
1526495506883 would post now...
1526495507084 in expiryDate validate: false
1526495507086 in expiryDate validate: false
#documentName的验证回调是同步执行的(调用validate后立刻触发回调),但#expiryDate的验证回调延迟了200ms才执行——这时候你的提交判断逻辑早就跑完了,formValid的修改对提交流程没有任何影响。
解决方案:用异步流程控制处理验证
你需要把所有验证操作包装成异步任务,等待所有验证完成后再决定是否提交。最常用的方式是使用Promise+Promise.all:
// 封装验证逻辑为Promise function validateField(fieldSelector) { return new Promise((resolve, reject) => { $(fieldSelector).validate(function(valid, elem) { valid ? resolve(true) : reject(false); }); }); } // 按钮点击事件处理器重构 $('.btn').on('click', function() { console.log(Date.now() + ' starting validation...'); // 等待所有验证完成 Promise.all([ validateField('#expiryDate'), validateField('#documentName') ]) .then(() => { // 所有验证通过,执行提交 console.log(Date.now() + ' would post now...'); // $.post(); }) .catch(() => { // 至少一个验证失败,阻止提交 console.log(Date.now() + ' no post...'); $('.btn').prop('disabled', false); }); });
如果你的环境支持ES2017+,也可以用async/await让代码更易读:
$('.btn').on('click', async function() { console.log(Date.now() + ' starting validation...'); try { await Promise.all([ validateField('#expiryDate'), validateField('#documentName') ]); // 验证通过 console.log(Date.now() + ' would post now...'); // $.post(); } catch (err) { // 验证失败 console.log(Date.now() + ' no post...'); $('.btn').prop('disabled', false); } });
额外提示
你可能需要确认一下你使用的表单验证插件(看起来是基于jQuery的自定义/第三方插件):
- 为什么
#expiryDate的验证是异步的?是否是因为datepicker插件的内部机制(比如选择日期后有异步的格式校验、远程校验)? - 插件是否支持同步验证的配置选项?如果有的话,可以尝试开启同步验证来简化逻辑。
总之,异步操作不能用同步的变量赋值来跟踪结果——必须用异步流程控制的方式(回调、Promise、async/await)来确保所有结果返回后再执行后续逻辑。
内容的提问来源于stack exchange,提问作者Nate Rightmire

