Comments (20)
throttle 和 debounce 的应用场景应该是分的很清楚的
- 按一个按钮发送 AJAX:给 click 加了 debounce 后就算用户不停地点这个按钮,也只会最终发送一次;如果是 throttle 就会间隔发送几次
- 监听滚动事件判断是否到页面底部自动加载更多:给 scroll 加了 debounce 后,只有用户停止滚动后,才会判断是否到了页面底部;如果是 throttle 的话,只要页面滚动就会间隔一段时间判断一次
from underscore-analysis.
@joesonw ,你说的 side effect 是可以消除的。其实不必要非得给 function 添加这个属性,只要是一个在 debounce 函数外部的变量就可以。高程三里的这个写法其实是可改成下面这个样子的:
var timer = null;
function debounce(method, context) {
clearTimeout(timer);
timer = setTimeout(function() {
method.call(context);
}, 1000);
}
function print() {
console.log('hello world');
}
window.onscroll = function() {
debounce(print);
};
为了避免对全局的污染,其实最好的方式是将 timer 放入函数中,成为一个局部变量,所以上面的写法可以改写成下面的方式:
function debounce(method, context) {
var timer = null;
return function() {
clearTimeout(timer);
timer = setTimeout(function() {
method.call(context);
}, 1000);
}
}
function print() {
console.log('hello world');
}
window.onscroll = debounce(print);
从这个意义上讲,闭包其实就是用来将两个内容隔离用的,将 timer 放入函数中,那么就需要将原来的语句放入函数中,使其与 timer 隔离,最近返回这个函数。结果就会和原来的效果是一样的。
from underscore-analysis.
改变了输入值, 给function多加了属性.
from underscore-analysis.
@oakland
恩, 这样就是接近underscore的方法的. 我的意思是原代码那样是会有隐患.
from underscore-analysis.
@joesonw 基础方法确实有隐患,如果传入的method是一个匿名函数,绑定到匿名函数的timer将不会被清理掉
from underscore-analysis.
最显而易见的是基础版每此触发事件都会取消定时器,然后重新设置定时器,而 underscore 中会在一定时间后才
取消定时器,重新设置定时器
我认为你这儿说的有问题,setTimeout是不精准延时,debounce里面补充判断如果last在[0,wait)区间,则继续setTimeout一个wait-last的时间再执行函数,保证函数执行程序一定在延时了wait之后执行。
from underscore-analysis.
基础版最重要的是, 有side effect吧.
from underscore-analysis.
@joesonw 请教下 side effect 具体是?
from underscore-analysis.
发现个问题,这个 clearTimeout() 直接让 setTimeout() 中的函数不执行,而不是调用 clearTimeout 之后立即执行里面的函数。也就是说,setTimeout() 的回调会在最后一次执行 debounce() 后起作用。这样就保证了只执行一次,就是节流啊。。。
from underscore-analysis.
@jsspace 我的理解是 「节流」(throttle)是控制函数执行的频率,而不是只执行一次(debounce)
from underscore-analysis.
哦哦,我弄混了
from underscore-analysis.
@riskers 不错,给 throttle 应用加了这个 case
from underscore-analysis.
debounce 有种 hold 住的感觉,一个动作不停地被触发,但是又不停地被终止,两次触发之间的时间长于给定的时间段才会真正触发这个时间。
不断触发又终止的过程,其实有点像卡带一样,不停地在重复一个声音,但是这个声音刚出来就被终止刚出来就被终止,直到不再卡带才会顺畅的播放一次。
上面的代码再做修改,可以发现,其实 debounce() 函数被触发了很多次,不过 print 函数被不断地触发禁止,触发禁止...
function debounce(method, context) {
var timer = null;
var n = 0;
return function() {
clearTimeout(timer);
timer = setTimeout(function() {
method.call(context);
}, 1000);
console.log(n++);
}
}
function print() {
console.log('hello world');
}
window.onscroll = debounce(print);
from underscore-analysis.
// 这里的 timeout 一定是 null 了吧
我感觉这里是这样的:func作为用户传入的任意函数,有可能会反过来调用debounce返回的新函数,比如
var func, de, i = 0;
func = function() {
i++;
if (i < 10) {
console.log(i);
de();
// setTimeout(de, 10);
}
};
de = _.debounce(func, 40);
de();
这个会输出1到9,改改条件应该就能出现 de -> func -> de这种嵌套调用了。
更新1:我才意识到我也把debounce当成节流了,抱歉。
更新2:那么debounce函数里应该可以判断immediate,如果是true则不用储存context / args了,正如最新的jashkenas/underscore:master里的写法。
那么,同理我发现底下的if (callNow)
有问题,可能会造成context和args被提前释放:
var f, d, tick = 0;
f = function() {
console.log('tick:', ++tick, [].slice.call(arguments, 0));
if (tick === 1) {
return d(1, 2) || 'tick-1 but d(1,2) returns empty';
}
return 'tick-' + tick;
};
d = _.debounce(f, 100, true);
var ret1 = d('ni hao');
console.log('first result', ret1);
输出是:
VM94:63 call now: begin with ["ni hao"]
VM94:77 tick: 1 ["ni hao"]
VM94:66 call now: end with [1, 2]
VM94:85 first result tick-1 but d(1,2) returns empty
demo见https://jsfiddle.net/emx3zdd9/1/
from underscore-analysis.
话说您用的understore 1.8.3 和 现在的jashkenas/underscore:master (https://github.com/jashkenas/underscore/blob/97cfcbcbbcedf544a13127dcca3e0ddad94ff830/underscore.js) 差了很多啊,_.debounce 完全被重写了。
我有个疑问是,master上的debounce已经在每次进入时就clearTimeout了,和您的“性能优化”的解释不一样,请问这两个方案的真正差别是什么?是应用场景导致的取舍吗?
- 我记得Chrome Developer Tools的Timeline里,每个setTimeout都要
0.3-0.5ms(补充:好像是30~50us)吧 - 我现在想对
<input>.oninput
做debounce (初步认为300ms比较好),已知用户打字够快且有时会连续输入大段文本,请问是否该每次清理计时器呢?
from underscore-analysis.
@gitwd 请问为什么匿名函数timer不会被清理?区别在哪里呢?
from underscore-analysis.
@hanzichi 韩老师你好!我想请教一下一个问题。
在您所阅读的underscore源码中(1.8.3),假设有如下代码:
var a = _.debounce((a)=>{
console.log(a);
}, 5000);
a(1);
a(2);
a(3);
a(4);
a(5);
a(6);
我的理解是:
a只有第一次被调用时才会进入later函数,但每次调用a都会更新时间戳Timestamp,而later内部会计算时间差,时间差不足时,递归调用later计算时间差,一旦时间差足够就触发传入的异步函数,最终执行的还是只有最后一个a函数。不知道正不正确?
我现在阅读的源码是最新版的,其中的_.debounce函数已经完全改进了,不再依赖于计算时间差,而是利用了JavaScript的异步机制:
假设有同样一段代码在最新版underscore中执行:
var a = _.debounce((a)=>{
console.log(a);
}, 5000);
a(1);
a(2);
a(3);
a(4);
a(5);
a(6);
我是否可以这样理解:
JavaScript优先执行完执行队列中的同步代码(以上所有代码)之后,再去执行事件队列中的异步代码。上方程序在执行所有同步代码时,每次a函数被调用,都会clearTimeout取消事件队列中的异步任务,导致前文a函数设置的异步任务被取消,直到最后一个a函数被执行时,才会开始计时,最终执行的也会是最后一个a函数。
两者相比较而言,后者使用变量更少,递归调用更少,数据计算更少;利用了JavaScript的异步机制,使用较少的代码较为自然的实现了去抖功能。
from underscore-analysis.
@hanzichi 韩老师您的文中还有一处小小的笔误:
// 设置 wait seconds 后触发 later 方法 // 无论是否 callNow(如果是 callNow,也进入 later 方法,去 later 方法中判断是否执行相应回调函数) // 在某一段的连续触发中,只会在第一次触发时进入这个 if 分支中 if (!timeout) // 设置了 timeout,所以以后不会进入这个 if 分支了 timeout = setTimeout(later, wait);
第一行注释中,wait seconds是否应该改为wait milliseconds?
冒昧啦!from underscore-analysis.
aswind7 commented on September 28, 2024 @gdh1995 请问为什么匿名函数timer不会被清理?区别在哪里呢?
/分割线/
因为每次执行throttle 都会创建一个新的匿名函数, 匿名函数身上没有tId.from underscore-analysis.
Trendymen commented on September 28, 2024 underscore版虽然不用每次触发时都清除计时器,但是每次触发时也使用Date对象重新生成了一个时间戳呀。
from underscore-analysis.
Related Issues (20)
- JavaScript 数组乱序 HOT 13
- 浅谈 underscore 内部方法 group 的设计原理 HOT 3
- 带注释的源码中var _ = function (obj) {....}中有个疑问 HOT 4
- 关于 bind 你可能需要了解的知识点以及使用场景 HOT 2
- bind 方法的兼容实现 HOT 1
- JavaScript 函数节流和函数去抖应用场景辨析 HOT 17
- underscore 函数节流的实现 HOT 8
- 从斐波那契数列求值优化谈 _.memoize 方法 HOT 6
- Function Functions 相关源码拾遗
- 浅谈 Web 中前后端模板引擎的使用 HOT 19
- Underscore _.template 方法使用详解 HOT 3
- Underscore 整体架构浅析 HOT 15
- 源码解读这半年 HOT 20
- 疑问:模块导出中,向后兼容老的require() API HOT 1
- 源码
- 打扰一下,有一个问题想咨询你一下 HOT 9
- 你好请教个问题 HOT 1
- 你好...在谷歌浏览器中运行`Object.prototype.toString().call(abb)`会报错 //var abb = true HOT 2
- 關於underscore源碼中提到了25個扩展方法之疑問? HOT 2
Recommend Projects
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
TensorFlow
An Open Source Machine Learning Framework for Everyone
Django
The Web framework for perfectionists with deadlines.
Laravel
A PHP framework for web artisans
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
Recommend Topics
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
web
Some thing interesting about web. New door for the world.
server
A server is a program made to process requests and deliver data to clients.
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Visualization
Some thing interesting about visualization, use data art
Game
Some thing interesting about game, make everyone happy.
Recommend Org
We are working to build community through open source technology. NB: members must have two-factor auth.
Microsoft
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba
Alibaba Open Source for everyone
D3
Data-Driven Documents codes.
Tencent
China tencent open source team.
from underscore-analysis.