You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
那么既然无法在当前主线程避免这个误差,我们能否另开一个线程去处理呢?当然可以,JavaScript 也提供给我们这样一个能力,通过 Web Worker 我们就可以在另一个线程来运行我们的代码。
// main.js varmyWorker=newWorker('worker.js');// 监听 worker myWorker.onmessage=function(e){result.textContent=e.data;console.log('Message received from worker');}first.onchange=function(){// 向 worker 发送数据 myWorker.postMessage([first.value,second.value]);console.log('Message posted to worker');}
// worker.js onmessage=function(e){// 接受主线程的数据 console.log('Message received from main script');varworkerResult='Result: '+(e.data[0]*e.data[1]);console.log('Posting message back to main script');// 向主线程发送数据 postMessage(workerResult);}
背景
我们都知道 setTimeout 是不准的。因为 setTimeout 是一个宏任务,它的指定时间指的是:进入主线程的时间。
所以什么时候可以执行
callback
,需要看 主线程前面还有多少任务待执行。我们可以通过这个场景来进行演示:
运行代码如下,通过一个计数器来记录每一次 setTimeout 的调用,而设定的间隔 * 计数次数,就等于理想状态下的延迟,通过以下例子来查看我们计时器的准确性。
我们如果在 setTimeout 还未执行期间加入一些额外的代码逻辑,再来看看这个差值。
可以看到随着时间的推移, setTimeout 实际执行的时间和理想的时间差值会越来越大,这就不是我们预期的样子。类比真实的场景,对于一些倒计时以及动画来说都会造成时间的偏差都是不理想的。
那么,从这个现象来看一下,为什么 setTimeout 会不准时呢?
因为我们的代码往往并不是只有一个 setTimeout,大多数会遇到以下情况。
requestAnimationFrame
该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行,回调函数执行次数通常是每秒60次,也就是每16.7ms 执行一次,但是并不一定保证为 16.7 ms。
我们也可以尝试一下将它来模拟 setTimeout。
发现由于 16.7 ms 间隔执行,在使用间隔很小的定时器,很容易导致时间的不准确。
再看看额外代码的引入效果。
略微加剧了误差的增加,因此这种方案仍然不是一种好的方案。
while
想得到准确的,我们第一反应就是如果我们能够主动去触发,获取到最开始的时间,以及不断去轮询当前时间,如果差值是预期的时间,那么这个定时器肯定是准确的,那么用 while 可以实现这个功能。
理解起来也很简单:
打印:误差 0
显然这样的方式很精确,但是我们知道 js 是单线程运行,使用这样的方式强行霸占线程会使得页面进入卡死状态,这样的结果显然是不合适的。
Web Worker
那么既然无法在当前主线程避免这个误差,我们能否另开一个线程去处理呢?当然可以,JavaScript 也提供给我们这样一个能力,通过 Web Worker 我们就可以在另一个线程来运行我们的代码。
那么接下来我们就要加 worker 和 while 相结合,以下为创建 worker 部分:
我们通过在 worker 中写入一个 while 循环,当达到我们的预取时间的时候,再向主线程发送一个完成事件,就不会因为主线程的其他代码的干扰而造成数据不准的情况。
我们来看一下实际的效果:
虽然我们用 Web Worker 修复时间看似被解决了。但是一方面, worker 线程会被 while 给占用,导致无法接受到信息,多个定时器无法同时执行,另一方面,由于 onmessage 还是属于事件循环内,如果主线程有大量阻塞还是会让时间越差越大,因此这并不是个完美的方案。
setTimeout 系统时间补偿
这个方案是在 stackoverflow 看到的一个方案
setTimeout系统时间补偿
当每一次定时器执行时后,都去获取系统的时间来进行修正,虽然每次运行可能会有误差,但是通过系统时间对每次运行的修复,能够让后面每一次时间都得到一个补偿。
因此通过系统的时间补偿,能够让我们的 setTimeout 变得更加准时,至此我们完成了如何让 setTimeout 准时的探索。
The text was updated successfully, but these errors were encountered: