setTimeout()
JavaScript语言是单线程语言,它有一个叫做执行队列的东西来决定代码的执行顺序,而定时器的作用是:在特定的时间后将代码插入到执行队列。
这里要特别理解:定时器setTimeout(function, Interval)这里的Interval是指当Interval个单位时间过去之后将代码function插入到执行队列中,而不是过去Interval个单位时间之后执行function代码。也就说明代码的执行的时间将大于等于Interval。
使用 setTimeout
设置循环任务:当 num === 10
的时候,任务完成
let num = 0;
let max = 10;
let incrementNumber = function () {num++console.log(num)if (num < max) {setTimeout(incrementNumber, 500)} else {alert('任务完成,奖励自己一朵小红花')}
}
setTimeout(incrementNumber, 500);
有了 setTimeout
,那 setInterval
是否可以完成同样的任务呢?
let num = 0, intervalId = null;
let max = 10;
let incrementNumber = function () {num++console.log(num)if (num === max) {clearInterval(intervalId)alert('任务完成,奖励自己一朵小红花')}
};
intervalId = setInterval(incrementNumber, 500);
但是呢,一般来说,setIntervale()
在实践中很少会在生产环境下使用
setInterval()
接收两个参数:要执行的代码(字符串或函数),以及把下一次执行定时代码的任务添加到队列要等待的时间(毫秒)。
这里的关键点是,第二个参数,也就是间隔时间,指的是向队列添加新任务之前等待的时间。比如,调用 setInterval()
的时间为 10:00:00
,间隔时间为 3000 毫秒。这意 味着 10:00:03
时,浏览器会把任务添加到执行队列。浏览器不关心这个任务什么时候执行或者执行要花多长时间。因为一个任务结束和下一个任务开始之间的时间间隔是无法保证的,有些循环定时任务可能会因此而被跳过。
动态计算时差 (仅针对循环定时,只起修正作用 )
var count = count2 = 0;
var runTime, runTime2;
var startTime, startTime2 = performance.now();//获取当前时间
//普通任务-对比
setInterval(function () {
runTime2 = performance.now();
++count2;
let time = runTime2 - (startTime2 + count2 * 1000)
console.log("普通任务", count2 + ' --- 延时:' + time + ' 毫秒');
}, 1000);
//动态计算时长
function func() {
runTime = performance.now();
++count;
let time = runTime - (startTime + count * 1000)
console.log("优化任务", count2 + ' --- 延时:' + time + ' 毫秒');
//动态修正定时时间
t = setTimeout(func, 1000 - time);
}
startTime = performance.now();
var t = setTimeout(func, 1000);
//耗时任务
setInterval(function () {
let i = 0;
while (++i < 100000000);
}, 0);
相差其实不大
Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。等到 Worker 线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务,被 Worker 线程负担了,主线程(通常负责 UI 交互)就会很流畅,不会被阻塞或拖慢。
将该文件直接放入浏览器中运行是会报错的,可以使用 live-server 在本地起一个服务器,这样 worker 才会正常运行
// worker.js
var count = 0;
var runTime;
var startTime = performance.now();
setInterval(function () {runTime = performance.now();++count;console.log("worker任务", count + ' --- 延时:' + (runTime - (startTime + 1000)) + ' 毫秒');startTime = performance.now();
}, 1000);
requestAnimationFrame是浏览器用于定时循环操作的一个接口,类似于setTimeout,主要用途是按帧对网页进行重绘。requestAnimationFrame 会把每一帧中的所有DOM操作集中起来,在一次重绘或回流中就完成,并且重绘或回流的时间间隔紧紧跟随浏览器的刷新频率,一般来说,这个频率为每秒60帧,即是时间间隔为1000/60=16.7ms。 requestAnimationFrame 与 setTimeout相比,最大的优势在于requestAnimationFrame是由系统来决定回调函数的执行时机,充分利用显示器的刷新机制,比较节省系统资源。当页面处于不可见或不可用状态时,浏览器就会停止动画,这意味着更少的CPU和更少的内存消耗。
问题:大家都知道 setTimeout 属于宏任务,那么 requestAnimationFrame 是属于宏任务还是微任务? 先来看一道题:
setTimeout(() => {console.log(1)
});
requestAnimationFrame(() => {console.log(2)
})
setTimeout(() => {console.log(4)
})
是 2 1 4 ?还是 1 4 2。 别猜,去浏览器中指行一下,你会发现一下子是 2 1 4,一下子又是 1 4 2。这是咋回事,于是你懵逼了。。。。
它既不是宏任务,又不是微任务。 一个 setTimeout 在 requestAnimationFrame 的前面,它们里面的两个回调的顺序是不确定的。
场景:每隔10ms设置图像向右移动1px
Document
在浏览器预览中可以看到,两个方块都是匀速前进的。 这时,在绘制之前,在其中添加耗时的任务
Document
在浏览器中,使用 setTimeout 的方块会出现卡顿,但是使用 requestAnimationFrame 则是非常顺畅。