同事打卡时OA系统卡了一下,疯狂点击签到按钮,结果发现出现多条同时间的打卡记录。所以要解决这种短时间内重复提交数据的问题。第一时间想到的是在前端做预防,直接设置签到按钮点击后一秒内变灰,不能点击。后来在网上查了一下,发现还有几种方式可以实现,下面列一下。

一、先了解一下幂等性

幂等(idempotent、idempotence)是一个数学与计算机学概念,常见于抽象代数中,即f(f(x)) = f(x).简单的来说就是一个操作多次执行产生的结果与一次执行产生的结果一致。有些系统操作天生就具有幂等性例如数据库的select语句,但更多时候是需要程序员来做保证的,尤其是在分布式系统环境中,接口能不能做到保证幂等性对系统的影响可能是非常大的,例如很常见的支付下单等场景,由于分布式环境中网络的复杂性,用户误操作,网络抖动,消息重复,服务超时导致业务自动重试等等各种情况都可能会使线上数据产生了不一致,造成生产事故。

这段话引用自这里

二、前端防重复提交

应用情景

经典使用情景:js的一些事件,比如:onresize、scroll、mousemove、mousehover等;

还比如:手抖、手误、服务器没有响应之前的重复点击;

这些都是没有意义的,重复的无效的操作,设置对整个系统的影响还可能是致命的,所以我们要对重复点击的事件进行相应的处理!

节流函数

所谓的节流函数顾名思义,就是某个时刻限制函数的重复调用。

同样节流函数也是为了解决函数重复提交的问题,而防止重复提交的方法,不止节流函数一种实现。

方法汇总

本文整理了我在工作实践当中,觉的防止js重复提交,比较好用的方法,在这里和大家分享一下。

一、setTimeout + clearTimeout(节流函数)

本文提供两种实现方式:普通节流函数和闭包节流函数

二、设定flag/js加锁

三、通过disable

四、添加浮层比如loading图层防止多次点击

具体实现

一、setTimeout + clearTimeout(节流函数)

/**
 * 闭包节流函数方法(可传参数)
 * @param Function fn 延时调用函数
 * @param Number delay 延迟多长时间
 * @return Function 延迟执行的方法
 */
var throttle = function (fn, delay) {
    var timer = null;
    return function () {
        var args = arguments; //参数集合
        clearTimeout(timer);
        timer = setTimeout(function () {
            fn.apply(this, args);
        }, delay);
    }
}

/**
 * 要执行的方法
 * @param String name 传递的参数
 */
function postFun(name) {
    document.writeln("名字:" + name);
}

//================测试部分 => 【1s重复点击10次】
var t = throttle(postFun, 1000);
var ejector = setInterval(() => {
    t("tiger");
}, 100);

setTimeout(() => {
    clearInterval(ejector);
}, 1000);

方式二:普通节流函数方法

/**
 * 普通节流函数方法
 * @param Function fn 延时调用函数
 * @param Number delay 延迟多长时间
 */
function throttle(fn, delay) {
    if (fn._id) {
        clearTimeout(fn._id);
    }
    fn._id = window.setTimeout(() => {
        fn();
        fn._id = null;
    }, delay);
}

/**
 * 要执行的方法
 */
function postFun() {
    document.writeln(new Date().getTime());
}

//================测试部分 => 【1s重复点击10次】
var interval = setInterval(() => {
    throttle(postFun, 1000);
}, 100);

setTimeout(() => {
    clearInterval(interval);
}, 1000);

二、设定flag/js加锁

var lock = false;
jQuery("#submit").on('click', function () {
    if (lock) {
        return false;
    }
   lock = true;
    jQuery.post(url, data, function (response) {
        //TODO:业务代码
        lock = false;
    });
});

总结
前两种方式实现起来比较方便,而后两种实现起来相对比较繁琐,如果是为了防止事件的多次触发,建议使用闭包,如果是表单提交,适度使用后两种比较稳妥。

引用自这里

三、后端防止数据重复提交

一、在数据库设计时业务字段加唯一约束,防止数据库插入重复数据。
二、在后端业务逻辑中,通过查询,判断该内容是否已经提交过

这种先查询db,再操作的也可以结合lock来做,检查及插入包含在lock块中,这种情况lock块中的逻辑不要太复杂,耗时太多会让其它的请求等待,并发性能就低了。

三、通过session的token来验证,一般不推荐使用