setTimeOut 和 setInterval对比
-
概念
在规定时间内自动执行某个函数;定时器又分为延时定时器和间歇定时器,即 setTimeOut 和 setInterval -
定时器的创建和清除
- 延时定时器
function fn(){ console.log('setTimemOut') } const timer = setTimeOut(fn, 100) // timer为定时器ID clearTimeOut(timer)
- 间歇定时器
function fn(){ console.log('setTimemOut') } const timer = setInterval(fn, 100) // timer为定时器ID clearInterval(timer)
-
用 setTimeOut 模拟 setInterval
let num = 100 function count(){ num -- timer = setTimeout(count,86400000);//递归调用延迟定时器 if(num == 0){ clearTimeout(timer);//清除定时器 } } timer = setTimeout(count,86400000);//创建延迟定时器
-
区别
setTimeOut 的间隔时间为设置的间隔时间加本身函数的执行时间,即 console 执行的时间 + 100function run() { console.log('其他代码执行时间') setTimeOut(function() { console.log(111) }, 100) }
setInterval 的间隔时间就是为设置的间隔时间,即就是 100
function run() { console.log('其他代码执行时间') setInterval(function() { console.log(111) }, 100) }
-
setTimeOut(fn, 0) 问题
javascript 为单线程,意味着所有的任务都需要排队,前一个任务完成后一个任务才会执行。
而浏览器为多线程,至少可以实现 3 个常驻线程:引擎线程、渲染线程、事件触发线程。- javascript 引擎是基于事件驱动单线程执行的,JS 引擎一直等待着任务队列中任务的到来,然后加以处理,浏览器无论什么时候都只有一个 JS 线程在运行 JS 程序。
- GUI 渲染线程负责渲染浏览器界面,当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行。但需要注意 GUI 渲染线程与 JS 引擎是互斥的,当 JS 引擎执行时 GUI 线程会被挂起,GUI 更新会被保存在一个队列中等到 JS 引擎空闲时立即被执行。
- 事件触发线程,当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待 JS 引擎的处理。这些事件可来自 JavaScript 引擎当前执行的代码块如 setTimeOut、也可来自浏览器内核的其他线程如鼠标点击、AJAX 异步请求等,但由于 JS 的单线程关系所有这些事件都得排队等待 JS 引擎处理。(当线程中没有执行任何同步代码的前提下才会执行异步代码)。
总结:所以当 js 代码遇到 setTimeOut 时,会把延时执行的函数置于任务队列中,当 js 引擎线程空闲时并到达延时的时间时,才会把函数置于 js 引擎线程中执行。拿 4 中的 run 来举例就是:首先浏览器会把 console.log()置于 js 引擎线程,然后把定时器置于任务队列中,当 console.log()执行完成,且没有其他的同步任务了,就会去任务队列中取定时器里面的任务置于 js 引擎线程中,然后执行它。
因此,setTimeout(fn,0)的含义是,指定某个任务在主线程最早可得的空闲时间执行,也就是说,尽可能早得执行。它在"任务队列"的尾部添加一个事件,因此要等到同步任务和"任务队列"现有的事件都处理完,才会得到执行。 -
setTimeOut 用处
- 函数防抖
const debounce = function(fn) { const timer = null return function() { let self = this if(timer) { clearTimeOut(timer) } timer = setTimeOut(function() { fn.call(this, arguments) }, 300) } }
- 轮询, 模拟 interval
setTimeOut(function() { console.log('执行') setTimeOut(arguments.callee, 100) })