Skip to content

异步编程的一个场景 #14

Open
@liusaint

Description

@liusaint

一个常见场景,点击保存,获取表单数据,发送请求。
一种防重复点击的策略:点击按钮,出现遮罩,提交请求。。。能不能保证只提交一次请求呢?

未点保存时页面处理很卡没有响应的情况呢?
点了保存后的操作导致页面无响应时继续点保存呢?

在表单超复杂,用户的系统配置低的情况下,获取表单数据可能会花很长一段时间,甚至让浏览器停止响应,这时保存操作导致浏览器停止响应而我们又再次点击了保存按钮,会触发点击事件吗?会在什么时机执行。特别是我们的点击事件触发了一个异步的保存操作的情况下。

看下面的代码。

    var btn = document.querySelector('button');
    btn.onclick = function(){
        console.log('事件响应')
        //页面卡5s
        var time = new Date().getTime();        
        while(new Date().getTime()-time < 5000){

        }
        setTimeout(function(){
            console.log('timeout');
        },0)        
    }

快速连续点5次按钮。
输出顺序:
‘事件响应‘ 每5s输出一个
‘timeout‘ 瞬间输出5个

一开始以为是点击事件的优先级比setTimeout要高,所以先执行。
后来发现dom点击事件与setTimeout在event loop都是属于macrotask。
是我的5次点击结束之后才会执行到setTimeout,所以是5次点击事件加入消息队列的顺序先于setTimeout,所以先执行。没毛病。

当我们的浏览器因为代码执行时间过长停止响应的时候,依然是可以往事件队列中添加事件的。来自用户操作,来自网络等。

加入遮罩。用display:none。替代。

    var btn = document.querySelector('button');
    btn.onclick = function(){
        console.log('事件响应')
        //页面卡5s
        var time = new Date().getTime();        
        while(new Date().getTime()-time < 5000){

        }
        btn.style.display = 'none';//隐藏
        setTimeout(function(){
            console.log('timeout');
        },0)        
    }

输出:
事件响应 1次
timeout 1次

所以是可以实现只点击一次的效果的。

然而,在我们的项目中,出现了保存多次的情况。 
观察发现,只是当开页面的那个瞬间狂点保存会出现保存多次的情况。同时我们的耗时操作是在异步操作中。分离场景如下:

    var btn = document.querySelector('button');
    btn.onclick = function(){
        console.log('事件响应')
        setTimeout(function(){
            //页面卡2s
            var time = new Date().getTime();
            while(new Date().getTime()-time < 2000){}
            btn.style.display = 'none';//隐藏按钮
            console.log('保存数据',new Date().getTime());
        },0) 
    }
    //加载完之后让页面卡5秒,在这5s中点击按钮。
    window.onload = function(){
        setTimeout(function(){
            console.log('卡页面,请在卡页面的时候点三次保存')
            var time = new Date().getTime();
            while(new Date().getTime()-time < 5000){}
            console.log('卡页面结束');
        },1000)

    }

在点保存之前页面就很卡的时候,连续点3次按钮:
事件响应 X 3
保存数据 1517486862740
保存数据 1517486864742
保存数据 1517486866742
页面正常的情况下连续点3次保存按钮,输出 事件响应 1次 保存数据 1次

回到上面的问题,遮罩防点击的思路用的时候并不是那么靠谱。所以我们主要考虑页面本来很卡的时候我们再点了多次保存,这个时候遮罩无法阻挡我们保存多次。要用其他的防护措施。比如加个class表示暂时不能点它。

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions