Skip to content

Commit

Permalink
add throttle
Browse files Browse the repository at this point in the history
  • Loading branch information
YvetteLau committed Jun 15, 2019
1 parent 775337b commit 7e8b123
Show file tree
Hide file tree
Showing 6 changed files with 606 additions and 0 deletions.
50 changes: 50 additions & 0 deletions Handwritten/src/debounce.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
function simpleDebounce(func, wait) {
let timer = null;
/* 触发时,参数传给了 debounced */
return function debounced() {
let args = arguments;
if (timer) clearTimeout(timer);
timer = setTimeout(() => {
/* this绑定和参数的传递,
* 注意此处使用的是箭头函数,因此不需要在外层将 this 赋值给某变量
*/
func.apply(this, args);
}, wait);
}
}


function debounce(func, wait, immediate = true) {
let timer;
// 延迟执行函数
const later = (context, args) => setTimeout(() => {
timer = null;// 倒计时结束
if (!immediate) {
func.apply(context, args);
//执行回调
context = args = null;
}
}, wait);
let debounced = function (...params) {
let context = this;
let args = params;
if (!timer) {
timer = later(context, args);
if (immediate) {
//立即执行
func.apply(context, args);
}
} else {
clearTimeout(timer);
//函数在每个等待时延的结束被调用
timer = later(context, args);
}
}
debounced.cancel = function () {
clearTimeout(timer);
timer = null;
};
return debounced;
};

export { simpleDebounce, debounce }
73 changes: 73 additions & 0 deletions Handwritten/src/throtte.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
/** 时间戳 */
function tampThrottle (func, delay) {
var lastTime = 0;
function throttled() {
var context = this;
var args = arguments;
var nowTime = Date.now();
if(nowTime > lastTime + delay) {
func.apply(context, args);
lastTime = nowTime;
}
}
//防抖函数最终返回的是一个函数
return throttled;
}

/** 定时器实现 */
function timeThrottle(func, wait) {
let timer;
return function throttled() {
let args = arguments;
if (!timer) {
timer = setTimeout(() => {
func.apply(this, args);
clearTimeout(timer);
timer = null;
}, wait);
}
}
}

function throttle(func, wait, options) {
var timeout, result;
var previous = 0;
if (!options) options = {};

var later = function (context, args) {
previous = options.leading === false ? 0 : Date.now() || new Date().getTime();
timeout = null;
result = func.apply(context, args);
};

var throttled = function () {
var now = Date.now() || new Date().getTime();
if (!previous && options.leading === false) previous = now;
var remaining = wait - (now - previous);
let context = this;
let args = arguments;
if (remaining <= 0 || remaining > wait) {
if (timeout) {
clearTimeout(timeout);
timeout = null;
}
previous = now;
result = func.apply(context, args);
if (!timeout) context = args = null;
} else if (!timeout && options.trailing !== false) {
// 判断是否设置了定时器和 trailing
timeout = setTimeout(() => { later(context, args) }, remaining);
}
return result;
};

throttled.cancel = function () {
clearTimeout(timeout);
previous = 0;
timeout = null;
};

return throttled;
}

export { tampThrottle, timeThrottle, throttle }
84 changes: 84 additions & 0 deletions Handwritten/test/debounce.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
<!DOCTYPE html>
<html>

<head lang="zh-CN">
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title></title>
<style>
.base {
height: 200px;
width: 200px;
background: pink;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}

p {
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
</style>
</head>

<body>
<div class="base">
<p>点击触发事件</p>
</div>
</body>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/lodash.min.js"></script>
<script>
window.onload = function () {
function debounce(func, wait, immediate = true) {
let timer;
// 延迟执行函数
const later = (context, args) => setTimeout(() => {
timer = null;// 倒计时结束
if (!immediate) {
func.apply(context, args);
//执行回调
context = args = null;
}
}, wait);
let debounced = function (...params) {
let context = this;
let args = params;
if (!timer) {
timer = later(context, args);
if (immediate) {
//立即执行
func.apply(context, args);
}
} else {
clearTimeout(timer);
//函数在每个等待时延的结束被调用
timer = later(context, args);
}
}
debounced.cancel = function () {
clearTimeout(timer);
timer = null;
};
return debounced;
};


function handleClick(e) {
console.log(this); //this值正确传递
console.log(e, [...arguments].splice(1)); //参数正确传递
}

/* 防抖,immediate = ture 每个等待时延的开始被调用 */
let handle = debounce(handleClick, 1000, true);
// let handle = _.debounce(handleClick, 1000, {
// leading: true,
// trailing: false
// });
document.querySelector('.base').onclick = handle;
}

</script>
165 changes: 165 additions & 0 deletions Handwritten/test/debounce.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
import { simpleDebounce, debounce } from '../src/debounce';
import { EventEmitter } from 'events';

/**
* 每 300 ms 触发一次 down 事件,共触发 3 次
* 防抖的间隔时间是 200ms,因此 frequency 被调用了 3次
*/
test('simpleDebounce/事件处理函数被调用3次 ', (done) => {
let myEvent = new EventEmitter();
/** 高频 down 事件处理函数 */
const frequency = jest.fn();
/** 间隔时间为 200ms */
let debounceFrequency = simpleDebounce(frequency, 200);

myEvent.on('down', () => {
debounceFrequency('hello');
});
/** 共触发 3 次 */
let i = 0;
function delay(callback, interval) {
let timer = setTimeout(() => {
if (i < 3) {
myEvent.emit('down');
delay(callback, interval);
i++;
} else {
clearTimeout(timer);
callback();
myEvent.removeAllListeners('down');
}
}, interval);
}

/**
* 每 300 ms 触发一次 down 事件,共触发 3 次
* 防抖的间隔时间是 200ms,因此 frequency 被调用了 3次
*/

delay(() => {
expect(frequency.mock.calls.length).toBe(3);
done();
}, 300);

});


/**
* 每 100 ms 触发一次 down 事件
* 防抖的间隔时间是 200ms,因此 frequency2 被调用了 0 次
*/
test('simpleDebounce/ 事件处理函数被调用0次 ', (done) => {
let myEvent = new EventEmitter();
/** 高频 down 事件处理函数 */
const frequency2 = jest.fn();
/** 防抖间隔时间为 200ms */
let debounceFrequency = simpleDebounce(frequency2, 200);
myEvent.on('down', () => {
debounceFrequency('hello');
});
/** 共触发 3 次 */
let i = 0;
function delay(callback, interval) {
let timer = setTimeout(() => {
if (i < 3) {
myEvent.emit('down');
delay(callback, interval);
i++;
} else {
clearTimeout(timer);
callback();
myEvent.removeAllListeners('down');
}
}, interval);
}

/**
* 每 100 ms 触发一次 down 事件
* 防抖的间隔时间是 200ms,因此 frequency2 被调用了 0 次
*/
//有时,这并不是我们想要的结果,我们可能希望最后一次/第一次的事件触发能够被响应
delay(() => {
expect(frequency2.mock.calls.length).toBe(0);
done();
}, 100);
});

/**
* 每 300 ms 触发一次 down 事件,共触发 3 次
* 防抖的间隔时间是 200ms,因此 frequency3 被调用了 3次
*/
test('debounce/ 事件处理函数被调用3次 ', (done) => {
let myEvent = new EventEmitter();
/** 高频 down 事件处理函数 */
const frequency3 = jest.fn();
/** 防抖间隔时间为 200ms */
let debounceFrequency = simpleDebounce(frequency3, 200);
myEvent.on('down', () => {
debounceFrequency('hello');
});
/** 共触发 3 次 */
let i = 0;
function delay(callback, interval) {
let timer = setTimeout(() => {
if (i < 3) {
myEvent.emit('down');
delay(callback, interval);
i++;
} else {
clearTimeout(timer);
callback();
myEvent.removeAllListeners('down');
}
}, interval);
}

/**
* 每 100 ms 触发一次 down 事件
* 防抖的间隔时间是 200ms,因此 frequency3 被调用了 0 次
*/
delay(() => {
expect(frequency3.mock.calls.length).toBe(3);
done();
}, 300);
});


/**
* 每 300 ms 触发一次 down 事件,共触发 3 次
* 防抖的间隔时间是 100ms, 第一次触发立即执行, frequency4 被调用了 1次
*/

test('debounce/ 事件处理函数被调用1次 ', (done) => {
let myEvent = new EventEmitter();
/** 高频 down 事件处理函数 */
const frequency4 = jest.fn();
/** 防抖间隔时间为 200ms/立即执行 */
let debounceFrequency = debounce(frequency4, 200, true);
myEvent.on('down', () => {
debounceFrequency('Yvette');
});
/** 共触发 3 次 */
let i = 0;
function delay(callback, interval) {
let timer = setTimeout(() => {
if (i < 3) {
myEvent.emit('down');
delay(callback, interval);
i++;
} else {
clearTimeout(timer);
callback();
myEvent.removeAllListeners('down');
}
}, interval);
}

/**
* 每 100 ms 触发一次 down 事件
* 防抖的间隔时间是 200ms,第一次触发立即执行,frequency4 仅被调用了 1 次
*/
delay(() => {
expect(frequency4.mock.calls.length).toBe(1);
done();
}, 100);
});
Loading

0 comments on commit 7e8b123

Please sign in to comment.