logo头像

arthas.com.cn

函数节流

该文章被围观

本文于345天之前发表,文中内容可能已经过时。

浏览器中某些计算和处理要比其他的昂贵很多。例如DOM操作比起非DOM交互需要更多的内存和CPU。连续尝试进行过多的DOM相关操作可能会导致浏览器挂起,有时候甚至奔溃。

函数节流背后的基本思想是指,某些代码不可以在没有间断的情况下连续重复执行。第一次调用函数,创建一个定时器,在指定的时间间隔之后运行代码。当第二次调用该函数时,它会清除前一个的定时器,并设置另一个。如果前一个定时器已经执行过了,这个操作就没有任何意思。然而,如果前一个定时器尚未执行,其实就是将其替换为一个新的定时器。目的是只有在执行函数的请求停止了一段时间之后才执行。

以下是该模式的基本形式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var processor = {
timeoutId: null,
performProcessing: function () {
// 实际执行的业务代码
},

// 初始处理调用的方法
process: function () {
clearTimeout(this.timeoutId);
var that = this;
this.timeoutId = setTimeout(function () {
that.performProcessing();
}, 100);
}
}

// 尝试开始执行
processor.process();

上面的过程可以通过throttle()函数简化:

1
2
3
4
5
6
function throttle(method, context) {
clearTimeout(method.tId);
method.tId = setTimeout(function () {
method.call(context);
}, 100);
}

throttle()函数接收两个参数,要执行的函数以及哪个作用域执行。
节流在resize事件中是最常用的。如果你基于该事件来改变页面布局的话,最好控制处理的频率,以确保浏览器不会在极短的时间内进行更多的计算。例如,假设有一个div元素需要保持它的始终等同于宽度,那么实现这一功能的JavaScript可以如下编写:

1
2
3
4
window.onresize = function () {
var div = document.getElementById('myDiv');
div.style.height = div.offsetWidth + 'px';
}

这样处理虽然能解决问题,但是会出现页面卡顿的情况,首先,如果该元素或者该页面的其他元素有非常复杂的CSS样式,那么这个过程将会很复杂。其次,设置元素的高度依然会很复杂,如果使用throttle()函数,将会大大得到优化:

1
2
3
4
5
6
7
8
function resizeDiv() {
var div = document.getElementById('myDiv');
div.style.height = div.offsetWidth + 'px';
}

window.onresize = function () {
throttle(resizeDiv);
}

只要代码是周期性执行的,都应该使用节流,但是你不能控制请求执行的频率。

参考资料:
《JavaScript高级程序设计》(第3版)