遇到了一道很有意思的题目:
完成如下操作: 首先打印“咚”,隔200ms打印“哒”,再隔150ms打印“哒”,再隔100ms打印“咚” 循环三次,每次循环间隔300ms
初看这道题 想法是使用定时器,但要注意不能直接使用循环 里面放定时器,因为这样由于setTimeout是宏任务,循环三次将三个定时器放入了宏任务队列,其实近似于三个回调函数同时执行,为了确保执行的顺序,每个打印任务都应该在任务队列中排列好,首先使用setTimeout来实现
function dong(){
console.log("咚")
}
function da() {
console.log("哒")
}
function handle() {
let count = 0;
const totalLoops = 3;
function loop() {
if (count >= totalLoops) return;
dong();
setTimeout(() => {
da();
setTimeout(() => {
da();
setTimeout(() => {
dong();
count++;
setTimeout(() => {
loop();
},300);
},100)
},150)
}, 200)
}
loop();
}
handle();
这里使用到了闭包来记录循环的次数,并且逐级嵌套,通过将任务都放入宏任务队列中来完成功能的实现。那自然想到能不能用微任务来实现,这里不考虑await/async的使用,使用promise实现
function handle() {
let count = 0;
const totalLoops = 3;
function loop() {
if (count >= totalLoops) return;
dong();
delay(200)
.then(() => {
da();
return delay(150);
})
.then(() => {
da();
return delay(100);
})
.then(() => {
dong();
count++;
if (count < totalLoops) {
return delay(300).then(loop); // 循环间隔 300ms
}
});
}
loop();
}
handle();
这段代码主要是利用了Promise.then()的回调函数放在微任务队列中的原理,这些方法放置到了微任务队列中。
上面的都用到了闭包的原理,下面再复习一下防抖与节流
- 防抖
在事件触发后执行,如果delay秒内函数再次被执行,那么重新计时,适合高频点击下只执行最后一次、搜索框联想输入
function debounce(fn,delay){
let timer = null;
return function (...args){
if(timer){
clearTimeout(timer);
timer = null;
}else{
fn.apply(this,args);
}
timer = setTimeout(() => {
timer = null;
},delay)
}
}
- 节流
执行函数n秒之后才重新执行函数,适合固定时间内只执行一次,防止超高频次触发位置变动,比如监控浏览器resize
function throttle(fn,delay){
let flag = true;
return function (...args){
if(!flag) return;
flag = false;
fn.apply(fn,delay)
setTimeout(() => {
flag = true;
},delay)
}
}