试试这么写节流和防抖

145 阅读2分钟

场景

注册页面的昵称输入框,平台规定昵称是唯一的,你在oninput里去后台查询昵称的唯一性,现在,你要优化它的查询次数。

1. 节流怎么做?

n毫秒内你只去后台检查一次。

2. 防抖怎么做?

依然是n毫秒内你只去后台检查一次,另,如果n毫秒内又发生了一次oninput,那么,你需要重置n。

实现

1. 节流的实现代码:

<input name="username" type="text" oninput="checkUsername(this)">
<script>
    var isRuning = false;
    function checkUsername(it) {
        if (!isRuning) {
            isRuning = true;
            // 可以在这里写昵称检查
            setTimeout(() => isRuning = false, 300)
        }
    }
</script>

2. 防抖的实现代码:

<input name="username" type="text" oninput="checkUsername(this)">
<script>
    var handler;
    function checkUsername(it) {
        clearTimeout(handler);
        handler = setTimeout(() => {
            // 可以在这里写昵称检查
        }, 300)
    }
</script>

重构

几个月之后你又遇到一个场景,需要做鼠标跟随特效。你想到节流和防抖,于是,你提炼如下:

1. 节流

<input name="username" type="text" oninput="checkUsername(this)">
<script>
    const throttle = (fun, wait) => {
        let isRuning = false;
        return (obj) => {
            if (!isRuning) {
                isRuning = true;
                setTimeout(() => isRuning = false, wait)
                fun(obj);
            }
        }
    }
    
    const checkUsername = throttle(x => {
        // 可以在这里写昵称检查
    }, 300);
</script>

2. 防抖

<input name="username" type="text" oninput="checkUsername(this)">
<script>
    const debounce = (fun, wait) => {
        let handle;
        return (obj) => {
            clearTimeout(handle);
            handle = setTimeout(() => fun(obj), wait);
        }
    }
    
    const checkUsername = debounce(x => {
        // 可以在这里写昵称检查
    }, 300);
</script>

另外的思考

又过了几个月,你看了一本书,书中提到自定义事件,你灵光乍现,是不是节流和防抖可以用自定义事件实现呢?

1. 节流

<input id="username" name="username" type="text">
<script>
    const throttle = (eventTarget, eventName, wait) => {
        let isRuning = false;
        let event = new Event(eventName);
        return (detail) => {
            if(isRuning) return;
            isRuning = true;
            setTimeout(() => isRuning = false, wait);
            event.detail = detail;
            eventTarget.dispatchEvent(event);
        }
    }

    let target = document.getElementById('username');
    target.addEventListener('input', throttle(target, 'checkUsername', 300))
    
    target.addEventListener('checkUsername', (event) => {
        // 可以在这里写昵称检查
    })
</script>

2. 防抖

<input id="username" name="username" type="text">
<script>
    const debounce = (eventTarget, eventName, wait) => {
        let handle;
        let event = new Event(eventName);
        return (detail) => {
            event.detail = detail;
            clearTimeout(handle);
            handle = setTimeout(() => eventTarget.dispatchEvent(event), wait);
        }
    }
    
    let target = document.getElementById('username');
    target.addEventListener('input', debounce(target, 'checkUsername', 300))

    target.addEventListener('checkUsername', (event) => {
        // 可以在这里写昵称检查
    })
</script>

总结

文字不多,代码偏多,如果没懂,拷贝下来试试就简单了。写法不止这些了,但是这种基于事件的思想我觉得很特别。基于事件的代码,可以更加解耦,可能别的代码也关心这个事件,但是不关心你昵称检查的代码。比如,可以封装一个ajax请求,成功或者失败都会触发事件,有的代码可能捕获这些事件来显示隐藏加载提示,有的代码希望记录日志。因为是基于事件,那么大家的代码互不影响。