关于 debounce 和 throttle

去抖( debounce ): 去除抖动,只执行一次
节流( throttle ): 限制执行次数,定时执行

在( scroll事件 + resize事件 + touchmove事件 + 实时搜索)可能会使用其中一个函数,但是这两个函数有什么区别呢?

首先,来看一下两个函数实现:

function debounce(fn, ts){
    var debounce_timer

    return function(){
        if (debounce_timer) {
           clearTimeout(debounce_timer)
           debounce_timer = null
        }
        debounce_timer = setTimeout(fn, ts || 1000)
    }
}

function throttle(fn, wait){
    var context,
        args,
        result,
        previous = 0,
        throttle_timer = 0,
        remaining
        wait = wait || 1000

    var later = function(){
        previous = Date.now()
        result = fn.apply(context, args)
    }

    return function(){
        context = this
        args = arguments

        if (!previous) {
            previous = Date.now()
        }
        remaining = wait - (Date.now() - previous)

        if (remaining <= 0) {
            if (throttle_timer) {
                clearTimeout(throttle_timer)
                throttle_timer = 0
            }
            previous = Date.now()
            result = fn.apply(context, args)
            context = args = null
        } else if (!throttle_timer){
            // 最后一次执行
            throttle_timer = setTimeout(later, remaining)
        }
        return result
    }
}

代码实现不难,本质都是利用定时器( setTimeout ),但是看起来,throttle 比 debounce 复杂一些。

通过一个试验来看一下两个函数分别有哪些特点。

通过监听 scroll、resize、touchmove 事件,使用 debounce 和 throttle 处理回调(添加内容)来进行测试

<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>debounce</title>
<style type="text/css">
body {
    font-size: 14px;
}

#main{
    height: 100vh;
    overflow: hidden;
}

.content {
    height: 100vh;
    overflow-y: auto;
}

</style>
</head>
<body>
<div id="main">
    <div class="content"></div>
</div>
</body>
<script type="text/javascript">
window.onload = addContent

var count = 0

var addContent = function(){
    var content = document.querySelector(".content")
    var html = []
    for (var i = 0; i < 15; i++) {
        var text = count + "离得近了肯德基分类的解放军的了解到了缴费的空间放开了的解放军大家都来缴费离得近"
        count += 1
        html.push("<p>" + text + "</p>")
    }

    var fragment = document.createElement('div')
    fragment.innerHTML = html.join(" ")
    content.appendChild(fragment)
}

var a = throttle(addContent, 1000) // 节流
var b = debounce("addContent", addContent, 1000) // 去抖

var addEvents = function(cb){
    ["scroll", "resize", "touchmove"].map(function(e){
        window.addEventListener(e, cb)
    })
}

addEvents(b)

</script>
</html>

经过测试,得到一下特点:

  • debounce 在触摸停止时,延迟一定时间执行一次回调函数
  • throttle 持续滚动时,定时执行回调函数,在触摸(滚动)停止时,执行了两次回调函数

体验上:

  • 使用 debounce 函数,添加内容有1秒的延迟
  • 使用 throttle 函数,添加内容没有延迟但是因为执行了两次,内容添加过多

总结:

个人觉得,在纠结是使用 debounce 还是 throttle 来处理重复触发的事件时,采用 debounce 函数来处理回调,通过调整延迟时间,体验比使用 throttle 函数体验更佳

标签: none

添加新评论