来看一个简单的定时器实现题,要求手写一个repeact()函数,使每3秒打印一个字符串,总共执行4次。
// 手写一个repeact()函数,加上下面的代码运行,使每3秒打印一个helloword,总共执行4次
const repeatFunc = repeact(console.log,4,3000)
repeatFunc('helloword')
一、做题之前需要知道的[1]
1、setTimeout( )和setInterval( )
setTimeout()方法设置一个定时器,该定时器在定时器到期后执行一个函数或指定的一段代码。var timeoutID = scope.setTimeout(function[ , delay, arg1, arg2, ...]);
setInterval()方法重复调用一个函数或执行一个代码段,在每次调用之间具有固定的时间延迟。var intervalID = scope.setInterval(func, delay, [arg1, arg2, ...]);
2、setTimeout( )和setInterval( )的运行机制
首先,setTimeout和setInterval属于事件循环中的宏任务,也就是说执行栈在执行完同步任务后,查看执行栈是否为空,如果执行栈为空,就会去检查微任务队列是否为空,如果为空的话,就执行宏任务,否则就一次性执行完所有微任务。
当然这和本题关系不大,在本题中我们只需要知道,在for循环中,for循环会先执行完(同步),这时setTimeout的回调全部塞入了事件队列中,然后for循环执行完毕后一起执行了。
3、为什么要用setTimeout( )实现setInterval( )[2]
setTimeout本身就是一次调用一次执行,setTimeout 不管上次异步任务是否完成,它都会将当前异步任务推入队列,而 setInterval 则会在任务推入异步队列时判断上次异步任务是否被执行。
setTimeout :延时delay毫秒之后,直接将回调函数加入事件队列。
setInterval :延时delay毫秒之后,先看看事件队列中是否存在还没有执行的回调函数(setInterval的回调函数),如果存在,就不要再往事件队列里加入回调函数了。
setTimeout 保证调用的时间间隔是一致的,setInterval的设定的间隔时间包括了执行回调的时间。
二、代码部分
直接开始动手撸代码~
我写了三种实现方式,欢迎补充👏
1、利用For循环和setTimeout()来实现
2、setTimeout()嵌套实现
3、setInterval()和 setTimeout()结合实现
1、利用For循环和setTimeout()来实现
for循环中用setTimeout()应该是最经典也是最容易的解法了。
实现的关键点在于,for循环一次碰到一个 setTimeout(),并不是马上把setTimeout()拿到异步队列中,而要等到delay时间到了后,才将其放到任务队列里面。for循环结束,这时执行到期的回调函数。
function repeact(func, count, time) {
return function (s) {
//执行循环count次,for循环会先执行完,将所有settimeout压入执行栈
//delay时间到了后 setTimeout的回调函数被放到任务队列,执行
for (let i = 0; i < count; i++) {
setTimeout(() => {
func(s)
}, time * (i + 1));
}
}
}
const repeatFunc = repeact(console.log, 4, 3000);
repeatFunc("helloword");
2、setTimeout()嵌套实现
设置两个setTimeout(),一个setTimeout()调用interval函数,interval函数包裹另一个setTimeout(),interval函数递归调用自己,每调用一次,count--,终止条件为count变量为0。
function repeact(func, count, time) {
return function (word) {
function interval(func, time) {
let interv = function () {
func(word)
count--
let timer = setTimeout(interv, time);
if (timer && count <= 0) {
clearTimeout(timer);
timer = null;
}
}
setTimeout(interv, time)
}
interval(func, time)
}
}
const repeatFunc = repeact(console.log, 4, 3000)
repeatFunc('helloword')
3、setInterval()和 setTimeout()结合实现
关键在于设置endTime,endTime = time * (count + 1),到期后setTimeout()将setInterval()清除。
function repeact(func, count, time) {
return function excu(words) {
let timer = setInterval(() => {
func(words)
}, time);
let endTime = time * (count + 1)
setTimeout(() => { clearInterval(timer) }, endTime);
}
}
const repeatFunc = repeact(console.log, 4, 3000)
repeatFunc('helloword')
结语
到这里就结束啦,欢迎在评论区留言讨论👏欢迎指出问题