防抖节流
22. 防抖节流
防抖和节流的概念其实最早并不是出现在软件工程中,防抖是出现在电子元件中,节流出现在流体流动中
而JavaScript是事件驱动的,大量的操作会触发事件,加入到事件队列中处理。
而对于某些频繁的事件处理会造成性能的损耗,我们就可以通过防抖和节流来限制事件频繁的发生;
1.防抖
175.jpg只有等待了一段时间再也没有事件触发之后,才会真正的执行响应函数
2. 防抖函数的应用场景
176.jpg3. 节流
177.jpg在一定时间之内,无论触发了多少次操作,执行函数的频率总是固定的
4. 节流函数的应用场景
178.jpg5. 防抖案例
179.jpg6.Underscore
180.jpg 181.jpgunderscore比较轻量级
debance
去抖动; 防反跳; 防抖动; 弹跳; 抖动消除;
throttle
n.节流阀; 节流杆; 风门; 风门杆;
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<input type="text">
<script src="https://cdn.jsdelivr.net/npm/underscore@1.13.2/underscore-umd-min.js"></script>
<script>
const inputEl = document.querySelector("input")
let counter = 0;
const inputChange = function() {
console.log(`发送了第${++counter}次网络请求`);
}
//不进行防抖节流处理
// inputEl.oninput = inputChange;
//防抖处理
//inputEl.oninput = _.debounce(inputChange, 2000)
//节流处理
inputEl.oninput= _.throttle(inputChange, 2000)
</script>
</body>
</html>
7.自定义防抖和节流函数
182.jpg1.基本实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div {
height: 100px;
width: 100px;
}
</style>
</head>
<body>
<input type="text" id="myinput">
<div id="myDiv"></div>
<script>
const input = document.querySelector("#myinput");
const mydiv = document.querySelector("#myDiv");
let counter = 0;
input.oninput = debounce(myFunc, 1000);
function myFunc() {
console.log(`发送了第${++counter}次网络请求`);
// mydiv.innerHTML = this.value;//发送请求
}
//在1s之内有没有被执行,被执行的话,再延迟1s,没有的话,就执行
//主要就是演出多久执行这个函数
function debounce(fn, delay) {
// 1.定义一个定时器, 保存上一次的定时器
let timer = null;
// 2.真正执行的函数
function _debounce(){
// 取消上一次的定时器
if(timer) {
clearTimeout(timer)
}
// 延迟执行
timer = setTimeout(() => {
// 外部传入的真正要执行的函数
fn()
}, delay)
}
return _debounce
}
</script>
</body>
</html>
2. 优化参数和this指向
有两个问题
1执行这个函数的this指向的不是input这个dom元素
2是返回的这个函数应该可以传入参数
_debounce这个函数的this应该指向的是input这个元素
因为相当于input.oninput = _debounce,
执行fn的时候,因为是独立调用,所以fn这个函数的this指向的是window,我们得使用apply等方法给他绑定this。
如果这里的settiemout是正常函数,可以通过bind绑定
// 延迟执行
timer = setTimeout(function () {
fn.apply(this)
}.bind(this), delay)
}
如果使用箭头函数的话,箭头函数不绑定this,就会找外层作用域的this
// 延迟执行
timer = setTimeout(() => {
fn.apply(this)
}, delay)
}
然后,关于event事件,
input.oninput = myFunc;
function myFunc(e) {
console.log(e);
console.log(`发送了第${++counter}次网络请求`);
// mydiv.innerHTML = this.value;//发送请求
}
fn是可以传入参数的,这个时候,就需要接受参数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div {
height: 100px;
width: 100px;
}
</style>
</head>
<body>
<input type="text" id="myinput">
<div id="myDiv"></div>
<script>
const input = document.querySelector("#myinput");
const mydiv = document.querySelector("#myDiv");
let counter = 0;
input.oninput = debounce(myFunc, 1000);
function myFunc(e) {
console.log(e);
console.log(`发送了第${++counter}次网络请求`);
// mydiv.innerHTML = this.value;//发送请求
}
//在1s之内有没有被执行,被执行的话,再延迟1s,没有的话,就执行
//主要就是演出多久执行这个函数
function debounce(fn, delay) {
// 1.定义一个定时器, 保存上一次的定时器
let timer = null;
// 2.真正执行的函数
function _debounce(...arg) {
// 取消上一次的定时器
if (timer) {
clearTimeout(timer)
}
// 延迟执行
timer = setTimeout(() => {
fn.apply(this, arg)
}, delay)
}
return _debounce
}
</script>
</body>
</html>
3.优化立即执行效果(第一次立即执行)
我们希望发送第一次的时候立即发送网络请求,其他后面的该延迟的就延迟。
比如用户一直连着输入,这时候timout一直取消,不会发送任何网络请求,这样不好,就是用户按下第一个以后就马上发第一个,展示点信息,不然用户以为搜索框不起作用。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div {
height: 100px;
width: 100px;
}
</style>
</head>
<body>
<input type="text" id="myinput">
<div id="myDiv"></div>
<script>
const input = document.querySelector("#myinput");
const mydiv = document.querySelector("#myDiv");
let counter = 0;
input.oninput = debounce(myFunc, 1000, true);
function myFunc(e) {
console.log(e);
console.log(`发送了第${++counter}次网络请求`);
// mydiv.innerHTML = this.value;//发送请求
}
//在1s之内有没有被执行,被执行的话,再延迟1s,没有的话,就执行
//主要就是演出多久执行这个函数
function debounce(fn, delay, immediate = false) {
// 1.定义一个定时器, 保存上一次的定时器
let timer = null;
let first = true;
// 2.真正执行的函数
function _debounce(...arg) {
if( first && immediate) {
fn.apply(this, arg);
first = false
}
// 取消上一次的定时器
if (timer) {
clearTimeout(timer)
}
// 延迟执行
timer = setTimeout(() => {
fn.apply(this, arg);
first = true;
}, delay)
}
return _debounce
}
</script>
</body>
</html>
4. 优化取消操作(增加取消功能)
如果有一个按键,在输入完1s之内,它按了取消按钮,这个请求不发送
函数对象上增加方法。
搜索框后面有时候有一个取消键,就是我们设置的timeout时间很长,用户按下取消键的时候,我们可以取消发送网络请求。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div {
height: 100px;
width: 100px;
}
</style>
</head>
<body>
<input type="text" id="myinput">
<div id="myDiv"></div>
<button>取消</button>
<script>
const input = document.querySelector("#myinput");
const mydiv = document.querySelector("#myDiv");
const myButton = document.querySelector("button")
let counter = 0;
const debounceChange = debounce(myFunc, 2000);
input.oninput = debounceChange;
myButton.onclick = function () {
debounceChange.cancle()
}
function myFunc(e) {
console.log(e);
console.log(`发送了第${++counter}次网络请求`);
// mydiv.innerHTML = this.value;//发送请求
}
//在1s之内有没有被执行,被执行的话,再延迟1s,没有的话,就执行
//主要就是延迟多久执行这个函数
function debounce(fn, delay, immediate = false) {
// 1.定义一个定时器, 保存上一次的定时器
let timer = null;
let first = true;
// 2.真正执行的函数
function _debounce(...arg) {
if (first && immediate) {
fn.apply(this, arg);
first = false
}
// 取消上一次的定时器
if (timer) {
clearTimeout(timer)
}
// 延迟执行
timer = setTimeout(() => {
fn.apply(this, arg);
first = true;
timer = null;
}, delay)
}
//封装取消功能
//函数本身也是对象,可以给函数添加属性
_debounce.cancle = function () {
if (timer) {
clearTimeout(timer);
timer = null;
first = true;
}
}
return _debounce
}
</script>
</body>
</html>
5.优化返回值
如果发送网络请求的响应函数有返回值呢?就是真正执行的myFunc如果有返回值的化,
有两种方法,1种是给函数在添加一个参数,callback
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div {
height: 100px;
width: 100px;
}
</style>
</head>
<body>
<input type="text" id="myinput">
<div id="myDiv"></div>
<button>取消</button>
<script>
const input = document.querySelector("#myinput");
const mydiv = document.querySelector("#myDiv");
const myButton = document.querySelector("button")
let counter = 0;
const debounceChange = debounce(myFunc, 2000, false, (res) => {
console.log("我是返回值" + res);
});
input.oninput = debounceChange;
myButton.onclick = function () {
debounceChange.cancle()
}
function myFunc(e) {
console.log(e);
console.log(`发送了第${++counter}次网络请求`);
// mydiv.innerHTML = this.value;//发送请求
return "abc"
}
//在1s之内有没有被执行,被执行的话,再延迟1s,没有的话,就执行
//主要就是延迟多久执行这个函数
function debounce(fn, delay, immediate = false, resultCallback) {
// 1.定义一个定时器, 保存上一次的定时器
let timer = null;
let first = true;
// 2.真正执行的函数
function _debounce(...arg) {
if (first && immediate) {
const result = fn.apply(this, arg);
first = false
if (resultCallback && typeof resultCallback === "function")
resultCallback(result)
}
// 取消上一次的定时器
if (timer) {
clearTimeout(timer)
}
// 延迟执行
timer = setTimeout(() => {
const result = fn.apply(this, arg);
first = true;
timer = null;
if (resultCallback && typeof resultCallback === "function")
resultCallback(result)
}, delay)
}
//封装取消功能
//函数本身也是对象,可以给函数添加属性
_debounce.cancle = function () {
if (timer) {
clearTimeout(timer);
timer = null;
first = true;
}
}
return _debounce
}
</script>
</body>
</html>
第二种方法就是使用promise
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
div {
height: 100px;
width: 100px;
}
</style>
</head>
<body>
<input type="text" id="myinput">
<div id="myDiv"></div>
<button>取消</button>
<script>
const input = document.querySelector("#myinput");
const mydiv = document.querySelector("#myDiv");
const myButton = document.querySelector("button")
let counter = 0;
const debounceChange = debounce(myFunc, 2000, false);
/* const tempCallback = () => {
console.log(this);//window
} */
const tempCallback = function(e){
console.log(this);
debounceChange.apply(this, [e]).then((res) => {
console.log("Promise的返回值结果",res);
})
}
input.oninput = tempCallback;
myButton.onclick = function () {
debounceChange.cancle()
}
function myFunc(e) {
console.log(e);
console.log(this);
console.log(`发送了第${++counter}次网络请求`);
// mydiv.innerHTML = this.value;//发送请求
return "abc"
}
//在1s之内有没有被执行,被执行的话,再延迟1s,没有的话,就执行
//主要就是延迟多久执行这个函数
function debounce(fn, delay, immediate = false) {
// 1.定义一个定时器, 保存上一次的定时器
let timer = null;
let first = true;
// 2.真正执行的函数
const _debounce = function(...arg) {
console.log("_debounce this :", this);
return new Promise((resolve, reject) => {
if (first && immediate) {
const result = fn.apply(this, arg);
resolve(result)
first = false
}
// 取消上一次的定时器
if (timer) {
clearTimeout(timer)
}
// 延迟执行
timer = setTimeout(() => {
const result = fn.apply(this, arg);
resolve(result)
first = true;
timer = null;
}, delay)
})
}
//封装取消功能
//函数本身也是对象,可以给函数添加属性
_debounce.cancle = function () {
if (timer) {
clearTimeout(timer);
timer = null;
first = true;
}
}
return _debounce
}
</script>
</body>
</html>
8. 自定义节流函数
在一定时间之内,无论触发了多少次操作,执行函数的频率总是固定的。
比如每10s执行一次,但是abcd输入完以后还没到10s。
先实现abcd输入完是第7秒,但是不执行,只有到第10s再执行。
这个时候距离最后一次输入距离3s之后再执行函数。如果输入完d是第10s,那么就马上执行。我们就就可以达到下面的公式。remaintime是10-(7-0)= 3,10-(10-0)=0
大于0的化就代表要等多久执行,小于0 或者等于0,就代表马上执行。
const remainTime = interval - (nowTime - startTime)
interval来自于传递来的函数,throttle函数。throttle(fn, interval)
nowTime = newDate().getTime()就是当前时间。我这次input的时候的这个当前时间。
first就是第一次是0,再第一次触发节流,发送网络事件以后,first就变成了interval。
[图片上传失败...(image-44b898-1665312315624)]
1. 基本实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style>
div {
height: 100px;
width: 100px;
}
</style>
</head>
<body>
<input type="text" id="myinput" />
<div id="myDiv"></div>
<button>取消</button>
<!-- <script src="https://cdn.jsdelivr.net/npm/underscore@1.13.2/underscore-umd-min.js"></script> -->
<script>
const input = document.querySelector("#myinput");
let counter = 0;
function myFunc() {
console.log(`发送了第${++counter}次网络请求`);
}
// input.oninput = _.throttle(myFunc, 10000);
//输入第一个的时候马上执行,然后没到interval时间之前等待执行。
//getTime()返回距 1970 年 1 月 1 日之间的毫秒数:
input.oninput = throttle(myFunc, 3000);
function throttle(fn, interval) {
//1.记录上一次执行函数的时间,也就是开始时间
let lastTime = 0;
const _throttle = function () {
//2.事件触发的时候真正执行的函数
//2.1获取当前事件触发时的时间
const nowTime = new Date().getTime();
//2.2 使用当前触发时间和时间间隔和上一次开始的时间,计算出还剩余多长时间需要去触发函数
const remainTime = interval - (nowTime - lastTime);
if (remainTime <= 0) {
//2.3 真正出发函数
fn();
//2.4保留上次触发的时间
lastTime = nowTime;
}
};
return _throttle;
}
</script>
</body>
</html>
再这个函数里。第一次输入框的时候,函数是立刻执行的 ,因为第一次的lasttime是0,也就是gettime是一个很大的值,那个remainTime = 3000 - (很大的值 - 0);一定是负数,所以一定会执行。
但是还有一个很大的bug就是如果最后一次输入的时间没有达到interval的时间,这个时候函数是不执行的。
就是我用3s输入完了,但是interval是10s,我等了7s,函数还是没有执行。
总结:
我们可以添加3个功能
第一个是第一次输入内容的时候触发不,leading
第二个参数是最后一次触发不触发,就是刚才的哪个bug
第三次是添加个一个callback,就是执行完函数之后,函数有返回值的情况。
我们把这些参数都放到options这个对象里面。
2. 优化立即执行效果
if (lastTime === 0 && leading === false) {
lastTime = nowTime;
}
html
这个第一次是第一次在框里面输入东西。如果间隔10s,第一下输入东西
,函数执行。然后第11s输入东西,函数马上执行,这个就不输入与这个第一次的范畴了。而是本身的基本实现里面就有。因为remainTime小于0 了。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style></style>
</head>
<body>
<input type="text" id="myinput" />
<script>
const input = document.querySelector("#myinput");
let counter = 0;
function myFunc() {
console.log(`发送了第${++counter}次网络请求`);
}
input.oninput = throttle(myFunc, 2000, { leading: false });
function throttle(
fn,
interval,
options = {
leading: true,
trading: true,
}
) {
const { leading, trading } = options;
//1.记录上一次执行函数的时间,也就是开始时间
let lastTime = 0;
const _throttle = function () {
//2.事件触发的时候真正执行的函数
//2.1获取当前事件触发时的时间
const nowTime = new Date().getTime();
//只有第一次的时候才会执行,因为只有第一次lastTime是0,才能造成很大的负数。
//不想第一次触发的化,就让第一次在输入框输入内容时候的time为当前时间,而不是为0
if (lastTime === 0 && leading === false) {
lastTime = nowTime;
}
//2.2 使用当前触发时间和时间间隔和上一次开始的时间,计算出还剩余多长时间需要去触发函数
const remainTime = interval - (nowTime - lastTime);
if (remainTime <= 0) {
//2.3 真正出发函数
fn();
//2.4保留上次触发的时间
lastTime = nowTime;
}
};
return _throttle;
}
</script>
</body>
</html>
3.优化节流最后一次也可以执行
比如10s的interval,第3s的时候输入完了,需要等7s后执行。
这个时候就是设置一个timeout,时间设置为7
setTimeout(fn(), 7000)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style></style>
</head>
<body>
<input type="text" id="myinput" />
<script>
const input = document.querySelector("#myinput");
let counter = 0;
function myFunc() {
console.log(`发送了第${++counter}次网络请求`);
}
input.oninput = throttle(myFunc, 3000, { leading: true, trading: true });
function throttle(
fn,
interval,
options = {
leading: false,
trading: true,
}
) {
const { leading, trading } = options;
//1.记录上一次执行函数的时间,也就是开始时间
let lastTime = 0;
let timer = null;
const _throttle = function () {
//2.事件触发的时候真正执行的函数
//2.1获取当前事件触发时的时间
const nowTime = new Date().getTime();
//只有第一次的时候才会执行,因为只有第一次lastTime是0,才能造成很大的负数。
//不想第一次触发的化,就让第一次在输入框输入内容时候的time为当前时间,而不是为0
//不想第一次执行就设置下面
if (lastTime === 0 && leading === false) {
lastTime = nowTime;
}
//2.2 使用当前触发时间和时间间隔和上一次开始的时间,计算出还剩余多长时间需要去触发函数
const remainTime = interval - (nowTime - lastTime);
if (remainTime <= 0) {
if (timer) {
clearTimeout(timer);
timer = null;
}
//2.3 真正出发函数
fn();
//2.4保留上次触发的时间
lastTime = nowTime;
return;
}
//我们只需要一个定时器就可以了,让它记住要给我们执行fn。如果再intrval之前再也没有在输入框输入内容的化。
if (trading && !timer) {
timer = setTimeout(() => {
fn();
timer = null;
//这里不能直接使用nowTime,因为我们需要的是执行settimeout函数的时候的时间,而不是设置settimeout的时间。
// lastTime = nowTime;
lastTime = new Date().getTime();
}, remainTime);
}
};
return _throttle;
}
</script>
</body>
</html>
4. 优化参数和this指向
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style></style>
</head>
<body>
<input type="text" id="myinput" />
<script>
const input = document.querySelector("#myinput");
let counter = 0;
function myFunc(event) {
console.log(`发送了第${++counter}次网络请求`);
console.log(this);
console.log(event.target.value);
}
input.oninput = throttle(myFunc, 3000, { leading: true, trading: true });
function throttle(
fn,
interval,
options = {
leading: false,
trading: true,
}
) {
const { leading, trading } = options;
let lastTime = 0;
let timer = null;
const _throttle = function (...args) {
const nowTime = new Date().getTime();
if (lastTime === 0 && leading === false) {
lastTime = nowTime;
}
const remainTime = interval - (nowTime - lastTime);
if (remainTime <= 0) {
if (timer) {
clearTimeout(timer);
timer = null;
}
fn.apply(this, args);
lastTime = nowTime;
return;
}
if (trading && !timer) {
timer = setTimeout(() => {
fn.apply(this, args);
timer = null;
lastTime = new Date().getTime();
}, remainTime);
}
};
return _throttle;
}
</script>
</body>
</html>
5.优化添加取消功能
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style></style>
</head>
<body>
<input type="text" id="myinput" />
<button id="btn">取消</button>
<script>
const input = document.querySelector("#myinput");
const btn = document.querySelector("#btn");
let counter = 0;
function myFunc(event) {
console.log(`发送了第${++counter}次网络请求`);
console.log(this);
console.log(event.target.value);
}
const _throttle = throttle(myFunc, 3000, {
leading: true,
trading: true,
});
input.oninput = _throttle;
btn.onclick = function () {
console.log("取消发送请求");
_throttle.cancle();
};
function throttle(
fn,
interval,
options = {
leading: false,
trading: true,
}
) {
const { leading, trading } = options;
let lastTime = 0;
let timer = null;
const _throttle = function (...args) {
const nowTime = new Date().getTime();
if (lastTime === 0 && leading === false) {
lastTime = nowTime;
}
const remainTime = interval - (nowTime - lastTime);
if (remainTime <= 0) {
if (timer) {
clearTimeout(timer);
timer = null;
}
fn.apply(this, args);
lastTime = nowTime;
return;
}
if (trading && !timer) {
timer = setTimeout(() => {
fn.apply(this, args);
timer = null;
lastTime = new Date().getTime();
}, remainTime);
}
};
_throttle.cancle = function () {
if (timer) {
clearTimeout(timer);
}
timer = null;
lastTime = 0;
};
return _throttle;
}
</script>
</body>
</html>
5.优化返回值问题
1.使用callback函数
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style></style>
</head>
<body>
<input type="text" id="myinput" />
<button id="btn">取消</button>
<script>
const input = document.querySelector("#myinput");
const btn = document.querySelector("#btn");
let counter = 0;
function myFunc(event) {
console.log(`发送了第${++counter}次网络请求`);
console.log(this);
console.log(event.target.value);
return "i am return value";
}
const _throttle = throttle(myFunc, 3000, {
leading: true,
trading: true,
resultCallback: (res) => {
console.log("resultCallback:", res);
},
});
input.oninput = _throttle;
btn.onclick = function () {
console.log("取消发送请求");
_throttle.cancle();
};
function throttle(
fn,
interval,
options = {
leading: false,
trading: true,
}
) {
const { leading, trading, resultCallback } = options;
//如果传来的options里面没有resultCallback的化,这时候解构出来的resultCallback就是undefined
let lastTime = 0;
let timer = null;
const _throttle = function (...args) {
const nowTime = new Date().getTime();
if (lastTime === 0 && leading === false) {
lastTime = nowTime;
}
const remainTime = interval - (nowTime - lastTime);
if (remainTime <= 0) {
if (timer) {
clearTimeout(timer);
timer = null;
}
const result = fn.apply(this, args);
if (resultCallback && typeof resultCallback === "function") {
resultCallback(result);
}
lastTime = nowTime;
return;
}
if (trading && !timer) {
timer = setTimeout(() => {
const result = fn.apply(this, args);
if (resultCallback && typeof resultCallback === "function") {
resultCallback(result);
}
timer = null;
lastTime = new Date().getTime();
}, remainTime);
}
};
_throttle.cancle = function () {
if (timer) {
clearTimeout(timer);
}
timer = null;
lastTime = 0;
};
return _throttle;
}
</script>
</body>
</html>
2.使用promise
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
<style></style>
</head>
<body>
<input type="text" id="myinput" />
<button id="btn">取消</button>
<script>
const input = document.querySelector("#myinput");
const btn = document.querySelector("#btn");
let counter = 0;
function myFunc(event) {
console.log(`发送了第${++counter}次网络请求`);
console.log(this);
console.log(event.target.value);
return "i am return value";
}
const _throttle = throttle(myFunc, 3000, {
leading: true,
trading: true,
resultCallback: (res) => {
console.log("resultCallback:", res);
},
});
const tempCallback = function (...args) {
console.log(this); //input这个元素
_throttle.apply(this, args).then((res) => {
console.log(res);
});
};
input.oninput = tempCallback;
btn.onclick = function () {
console.log("取消发送请求");
_throttle.apply(this).cancle();
};
function throttle(
fn,
interval,
options = {
leading: false,
trading: true,
}
) {
const { leading, trading, resultCallback } = options;
//如果传来的options里面没有resultCallback的化,这时候解构出来的resultCallback就是undefined
let lastTime = 0;
let timer = null;
const _throttle = function (...args) {
return new Promise((resolve, reject) => {
const nowTime = new Date().getTime();
if (lastTime === 0 && leading === false) {
lastTime = nowTime;
}
const remainTime = interval - (nowTime - lastTime);
if (remainTime <= 0) {
if (timer) {
clearTimeout(timer);
timer = null;
}
const result = fn.apply(this, args);
resolve(result);
lastTime = nowTime;
return;
}
if (trading && !timer) {
timer = setTimeout(() => {
const result = fn.apply(this, args);
resolve(result);
timer = null;
lastTime = new Date().getTime();
}, remainTime);
}
});
};
_throttle.cancle = function () {
if (timer) {
clearTimeout(timer);
}
timer = null;
lastTime = 0;
};
return _throttle;
}
</script>
</body>
</html>