• setTimeout() 和 setIntervale() 小结


    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); 
    
    • 1
    • 2
    • 3
    • 4
    • 5

    有了 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); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    但是呢,一般来说,setIntervale() 在实践中很少会在生产环境下使用

    setInterval() 接收两个参数:要执行的代码(字符串或函数),以及把下一次执行定时代码的任务添加到队列要等待的时间(毫秒)。

    这里的关键点是,第二个参数,也就是间隔时间,指的是向队列添加新任务之前等待的时间。比如,调用 setInterval() 的时间为 10:00:00,间隔时间为 3000 毫秒。这意 味着 10:00:03 时,浏览器会把任务添加到执行队列。浏览器不关心这个任务什么时候执行或者执行要花多长时间。因为一个任务结束和下一个任务开始之间的时间间隔是无法保证的,有些循环定时任务可能会因此而被跳过。

    如何消除不准确

    动态计算时差 (仅针对循环定时,只起修正作用 )

    • 在定时器开始前和运行时动态获取当前时间,在设置下一次定时时长时,在期望值基础上减去当前时延,以获得相对精准的定时运行效果。
    • 此方法仅能消除setInterval()长时间运行造成的误差累计,但无法消除单个定时器执行延迟问题。
    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); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    相差其实不大

    使用 Web Worker

    Web Worker 的作用,就是为 JavaScript 创造多线程环境,允许主线程创建 Worker 线程,将一些任务分配给后者运行。在主线程运行的同时,Worker 线程在后台运行,两者互不干扰。等到 Worker 线程完成计算任务,再把结果返回给主线程。这样的好处是,一些计算密集型或高延迟的任务,被 Worker 线程负担了,主线程(通常负责 UI 交互)就会很流畅,不会被阻塞或拖慢。

    将该文件直接放入浏览器中运行是会报错的,可以使用 live-server 在本地起一个服务器,这样 worker 才会正常运行

    
    
    
    
    
    
    
     
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    // 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); 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    requestAnimationFrame

    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)
    }) 
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    是 2 1 4 ?还是 1 4 2。 别猜,去浏览器中指行一下,你会发现一下子是 2 1 4,一下子又是 1 4 2。这是咋回事,于是你懵逼了。。。。

    它既不是宏任务,又不是微任务。 一个 setTimeout 在 requestAnimationFrame 的前面,它们里面的两个回调的顺序是不确定的。

    场景:每隔10ms设置图像向右移动1px

    
    
    
    Document
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在浏览器预览中可以看到,两个方块都是匀速前进的。 这时,在绘制之前,在其中添加耗时的任务

    
    
    
    Document
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在浏览器中,使用 setTimeout 的方块会出现卡顿,但是使用 requestAnimationFrame 则是非常顺畅。

  • 相关阅读:
    数组对象出现的新功能
    Redis企业版数据库如何支持实时金融服务?
    JavaScript 实现点击/关闭全屏
    【项目管理】--敏捷开发管理之Scrum
    内点法(interior point method)求解二次规划,附python代码
    频谱和功率谱的区别与联系
    华为机试 - 按索引范围翻转文章片段
    【服务器数据恢复】EMC Unity存储误操作删除数据卷的数据恢复案例
    使用C语言实现双向链表(带头结点)
    [附源码]Python计算机毕业设计Django甜品购物网站
  • 原文地址:https://blog.csdn.net/web2022050901/article/details/126390633