摘要:在函数开始执行时,立刻设置一个“正在处理中”的标志位。在函数执行完毕(无论成功或失败)后,再将该标志位重置。所有后续的点击都会因为检查到这个标志位而直接被忽略。
在开发网页或应用程序时,我们经常会遇到一个经典场景:用户因为网络延迟或心情急切,疯狂地点击一个提交按钮。
如果处理不当,这可能会导致灾难性的后果——比如重复创建订单、发送多份相同的表单数据、或者对数据库造成不必要的压力。
这个问题虽然常见,但解决方法却多种多样。
这是最直观、最容易理解的方法。我们通过一个标志位(flag)来控制函数的执行。
在函数开始执行时,立刻设置一个“正在处理中”的标志位。在函数执行完毕(无论成功或失败)后,再将该标志位重置。所有后续的点击都会因为检查到这个标志位而直接被忽略。
let isSubmitting = false; // 状态锁async function handleSubmit { if (isSubmitting) { console.log('正在处理中,请勿重复提交...'); return; } isSubmitting = true; console.log('表单提交中...'); try { // 模拟一个异步请求,例如向服务器发送数据 await new Promise(resolve => setTimeout(resolve, 2000)); console.log('表单提交成功!'); // 在这里处理成功后的逻辑,比如页面跳转 } catch (error) { console.error('提交失败:', error); // 处理错误 } finally { // 无论成功还是失败,最后都要“解锁” isSubmitting = false; }}document.getElementById('submitBtn').addEventListener('click', handleSubmit);优点在于简单易懂,实现成本低,且适用于大多数简单的场景。
这个方法不仅在逻辑上阻止了重复点击,还为用户提供了最直观的视觉反馈。
当用户第一次点击按钮后,立即将按钮的 disabled 属性设为 true。这样浏览器层面就会阻止任何后续的点击事件,当操作完成后,再将按钮恢复为可用状态。
const submitBtn = document.getElementById('submitBtn');async function handleSubmit { // 禁用按钮 submitBtn.disabled = true; submitBtn.textContent = '提交中...'; // 优化用户体验 try { await new Promise(resolve => setTimeout(resolve, 2000)); console.log('提交成功!'); } catch (error) { console.error('提交失败:', error); } finally { // 恢复按钮 submitBtn.disabled = false; submitBtn.textContent = '提交'; }}submitBtn.addEventListener('click', handleSubmit);这个方案实现简单,用户能清晰地看到按钮不可用,明白他们的请求正在被处理。
节流是一种更通用、更优雅的解决方案,它能将高频触发的事件,限制在一定时间间隔内只执行一次。
这完美符合我们“立即响应第一次,忽略后续”的需求。
首先,我们需要一个节流工具函数。
/** * 节流函数 * @param {Function} func 要执行的函数 * @param {number} delay 冷却时间(毫秒) * @returns {Function} 返回一个新的节流函数 */function throttle(func, delay) { let timer = null; return function(...args) { if (!timer) { func.apply(this, args); timer = setTimeout( => { timer = null; }, delay); } };}使用节流包装我们的提交函数:
// 原始的提交函数function submit { console.log('请求已发送!'); // 模拟异步请求 new Promise(resolve => setTimeout(resolve, 1000)).then( => { console.log('服务器响应了。'); });}// 使用节流包装我们的提交函数,设置2秒的冷却时间const throttledSubmit = throttle(submit, 2000);document.getElementById('submitBtn').addEventListener('click', throttledSubmit);这种模式功能强大,适用于各种高频触发场景(如scroll, resize)。
在实际项目中,我们往往会组合使用这些方案以达到最佳效果,禁用按钮 + 节流函数 + 状态锁多重组合。
节流作为逻辑层的兜底,确保即使UI状态出现问题,函数调用频率依然受控,禁用按钮作为UI层的反馈,给用户最清晰的指引。
来源:不秃头程序员