博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
[Javascript] 实现setInterval函数
阅读量:6835 次
发布时间:2019-06-26

本文共 3047 字,大约阅读时间需要 10 分钟。

问题

经常使用Javascript的同学一定对setInterval非常熟悉,当使用setInterval(callback, timer)时,每经过timer毫秒时间,系统都将调用一次callback。请问全局如果没有提供setInterval函数,该如何自己实现这一功能?

方案一:循环或递归(错误解法)

最简单的思路便是通过简单的循环或者递归,每次检查时间戳是否已经超过上次触发给定函数的时间加上间隔时间,如果已经超过便再次触发函数,并重置计时器至当前时间。

const setInterval1 = (func, interval) => {    let startTime = Date.now();    const config = { shouldStop: false };    while (!config.shouldStop) {        if (Date.now() - startTime >= interval) {            func();            startTime = Date.now();        }    }    return config;}const myClearInterval = config => { config.shouldStop = true; }

然而这样的解法有一个致命问题,我们将setInterval1变成一个阻塞函数,主线程会卡死在这个无限循环或者递归中,导致之后的代码或者事件无法执行。想了解详细原因的请戳: ,

方案二:使用setTimeout

setTimeout的好处在于,它是在消息队列里面添加一个待执行的消息,所以并不会堵塞主线程。更方便的在于,由于setTimeout自带定时器功能,我们甚至不用自己去维护一个时间戳。我们可以通过不断递归调用setTimeout来实现setInterval的效果

const setInterval2 = (func, interval) => {    const config = { shouldStop: false }    const loop = () => {        if (!config.shouldStop) {            func();            setTimeout(loop, interval);        }    }    setTimeout(loop, interval);    return config;}const myClearInterval = config => { config.shouldStop = true; }

方案三:使用requestAnimationFrame

然而使用setTimeout有违这道题的初衷,因为setTimeout在本质上和setInterval是类似的,多少有些作弊的嫌疑。那有没有别的非阻塞方案呢?在浏览器环境中,我们有,而在nodejs环境中,我们有setImmediate()。以requestAnimationFrame为例,这将保证我们的代码只会在每一帧render之前被递归一次,从而避免了阻塞其他代码。

const setInterval3 = (func, interval) => {    let startTime = Date.now();    const config = { shouldStop: false }    const check = () => {        if (!config.shouldStop) {            if (Date.now() - startTime > interval) {                func();                startTime = Date.now();            }            if(typeof window === 'undefined') {                setImmediate(check);            } else {                window.requestAnimationFrame(check)            }        }    }    check();    return config;}const myClearInterval = config => { config.shouldStop = true; }

方案四:使用Web Worker

requestAnimationFrame能确保我们在每帧显示前被调用一次,从而检计时器是否到期,但是如果被执行的函数计算量极大,,该如何保证给定函数能按时执行呢?显然,此时只依靠主线程来确保计时程序和给定程序都能准确执行,有点困难,但是如果将计时程序放入另一线程中,而主程序只负责监听定时器事件和执行给定程序,是不是会好一些呢?所以我们这里利用浏览器提供的来实现多线程。请注意这里由于没有调用另一个脚本,我们通过blob和object url的方式将我们的定时器程序check传入Web Worker中。

const setInterval4 = (func, interval) => {    if (typeof window !== 'undefined' && window.Worker && window.Blob) {        const check = new Blob(["(", function(){            self.onmessage = function(e) {              const interval = e.data;            let startTime = Date.now();            while(true) {                if (Date.now() - startTime >= interval) {                  startTime = Date.now();                self.postMessage(Date.now());              }            }          }        }.toString(), ")()"], { type: "text/javascript" });        const worker = new Worker(window.URL.createObjectURL(check));        worker.onmessage = func;        worker.postMessage(interval);                return worker;    } else {        console.log('Your environment is not supported');    }}const myClearInterval = config => { config.terminate() }

转载地址:http://xjxkl.baihongyu.com/

你可能感兴趣的文章
Oracle临时表空间使用分析
查看>>
Web 访问日志分析
查看>>
Android更改Activity样式和隐藏标题
查看>>
Zabbix 4.0,监控Tomcat(4)
查看>>
javascript console使用说明
查看>>
jQuery1.9+版本的.on使用方法笔记
查看>>
我的友情链接
查看>>
1.MyBaits 3.2 简介
查看>>
hibernate缓存
查看>>
防火墙侦测一下地址是否有经过
查看>>
Jmeter录制app脚本
查看>>
解决java.lang.RuntimeException: mapped-name is required for xxx.xxx.xxx/xx of deployment xx.war
查看>>
impdp导入数据报错"ORA-39029"处理一例
查看>>
shell脚本--语法篇
查看>>
多态实现原理
查看>>
弱链接和链接期错误
查看>>
CentOS6.3上SSH远程登录实现无密码认证
查看>>
java起源和基本数据类型
查看>>
向Flash传值的两种方式
查看>>
CCNP学习笔记之EIGRP 上
查看>>