摘要:更方便的在于,由于自带定时器功能,我们甚至不用自己去维护一个时间戳。请注意这里由于没有调用另一个脚本,我们通过和的方式将我们的定时器程序传入中。
问题
经常使用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变成一个阻塞函数,主线程会卡死在这个无限循环或者递归中,导致之后的代码或者事件无法执行。想了解详细原因的请戳: 并发模型与事件循环,JavaScript:彻底理解同步、异步和事件循环(Event Loop)
方案二:使用setTimeoutsetTimeout的好处在于,它是在消息队列里面添加一个待执行的消息,所以并不会堵塞主线程。更方便的在于,由于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是类似的,多少有些作弊的嫌疑。那有没有别的非阻塞方案呢?在浏览器环境中,我们有requestAnimationFrame(),而在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能确保我们在每帧显示前被调用一次,从而检计时器是否到期,但是如果被执行的函数计算量极大,导致帧内无法完成时,该如何保证给定函数能按时执行呢?显然,此时只依靠主线程来确保计时程序和给定程序都能准确执行,有点困难,但是如果将计时程序放入另一线程中,而主程序只负责监听定时器事件和执行给定程序,是不是会好一些呢?所以我们这里利用浏览器提供的Web Worker API来实现多线程。请注意这里由于没有调用另一个脚本,我们通过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() }
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/93884.html
摘要:定义对象,用于保存映射到真实每调用一次就会自增的一个这里注意要使用局部变量保存哦,避免函数内部直接引用,因为可能会再次变化说明使用时除了需要加上一个对象做命名空间外其实也是没办法哦,因为和需要共享一个叫做的映射表,其他与直接调用原生,无异举 talk is cheap: var util = (function(){ //定义intervalObj对象,用于保存interval...
摘要:单线程的话,如果我们做一些的操作比如说这是一个耗时的操所那么在这将近一秒内,线程就会被阻塞,无法继续执行下面的任务。事件循环的主要机制就是任务队列机制一个事件循环有一个或者多个任务队列。 浏览器中的事件循环机制 网上一搜事件循环, 很多文章标题的前面会加上 JavaScript, 但是我觉得事件循环机制跟 JavaScript 没什么关系, JavaScript 只是一门解释型语言, ...
摘要:主线程会暂时存储等异步操作,直接向下执行,当某个异步事件触发时,再通知主线程执行相应的回调函数,通过这种机制,避免了单线程中异步操作耗时对后续任务的影响。 背景 在研究js的异步的实现方式的时候,发现了JavaScript 中的 macrotask 和 microtask 的概念。在查阅了一番资料之后,对其中的执行机制有所了解,下面整理出来,希望可以帮助更多人。 先了解一下js的任务执...
摘要:一什么是定时器提供了一些原生方法来实现延时去执行某一段代码,下面来简单介绍一下设置一个定时器,在定时器到期后执行一次函数或代码段定时器延迟后执行的函数延迟后执行的代码字符串,不推荐使用原理类似延迟的时间单位毫秒,默认值为向延迟函数传递而外的 一、什么是定时器 JS提供了一些原生方法来实现延时去执行某一段代码,下面来简单介绍一下 setTimeout: 设置一个定时器,在定时器到期后执行...
阅读 2068·2021-09-22 15:54
阅读 1833·2021-09-04 16:40
阅读 856·2019-08-30 15:56
阅读 2627·2019-08-30 15:44
阅读 2151·2019-08-30 13:52
阅读 1123·2019-08-29 16:35
阅读 3342·2019-08-29 16:31
阅读 2565·2019-08-29 13:48