携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第32天,点击查看活动详情
setTimeout
是什么
setTimeout是定时器,在实际开发中是很常用的。用来指定某个函数多少秒后执行。
我们都知道,虽然我们定义了具体的毫秒数,但未必就是在定义的毫秒数结束后就立即执行了,因为我们每一个任务需要放入队列中等待后执行。
返回值
它会返回一个整数,表示的是定时器的编号。我们可以用这个编号来取消定时器。
let timerId = setTimeout(() => {
console.log('juejin...')
}, 300)
console.log(timerId) // 14100
如果在定时器被执行之前需要取消执行定时器函数,可以执行clearTimeout函数。
clearTimeout(timerId)
在浏览器中是如何实现的
众所周知,JavaScript是单线程工作的。在执行多个任务时,例如接下来需要执行:
- 一段异步JS代码
- 添加或删除一个DOM节点
- 改变浏览器窗口大小
这三个任务会被放入消息队列排队执行,当然如果是异步操作,事件循环系统也会不断把任务加入到消息队列中循环执行。
任务是怎么执行的我们清楚了,那定时器函数为什么会在延迟时间后被执行呢?我们我们把延迟任务放入刚才说的消息队列中,那时间误差肯定会很大,特别是遇到同步任务比较费时的任务,那肯定被延误了,所谓的定时执行也无从谈起。
其实在浏览器中除了普通的消息队列之外,还有另外一个比较特殊的队列:延迟队列。里面都是存的需要延迟的任务列表。包括定时器和Chromium内部一些需要延迟的任务。
如下代码:
延迟任务都是放到delayed_incoming_queue队列中,ProcessTimerTask函数执行的就是定时或延迟任务,根据配置的延迟时间到了之后就会执行。
void ProcessTimerTask(){
// 从delayed_incoming_queue中取出已经到期的定时器任务
// 依次执行这些任务
}
TaskQueue task_queue;
void ProcessTask();
bool keep_running = true;
void MainTherad(){
for(;;){
//执行消息队列中的任务
Task task = task_queue.takeTask();
ProcessTask(task);
//执行延迟队列中的任务
ProcessDelayTask()
if(!keep_running) //如果设置了退出标志,那么直接退出线程循环
break;
}
}