setTimeout,setInterval ,requestAnimationFrame定时器
定时器函数通常用于执行定时任务,也就是说你做了一个功能放在定时器函数里,它可以在特定的时间去执行你的指令,或者说隔多长时间(单位时间内—毫秒为单位)去执行。
💫在异步编程中 定时函数也是不可缺席的,常见的定时器有: setTimeout
,setInterval
,requestAnimationFrame
。
🏃setTimeout
多人认为 setTimeout
是延时多久后才开始执行函数,其实这里存在一些误解:
- 1️⃣精确延时
-
误解
:很多人认为 setTimeout 会在指定的毫秒数后精确地执行回调函数。
事实
:实际上,setTimeout 只是告诉 JavaScript 引擎在指定的时间之后尝试执行回调函数。由于 JavaScript 是单线程的,如果在这段时间内主线程上有其他任务在运行,那么回调函数的执行会被推迟到当前任务完成之后。 2️⃣ 立即执行 -
误解
:有些人认为将延迟设置为 0 毫秒(setTimeout(callback, 0))会让回调函数立即执行。
事实
:即使延迟设置为 0 毫秒,回调函数也会被推入事件队列,并在当前执行栈清空后执行。这是因为 JavaScript 的事件循环机制决定了任何 setTimeout 调用都会进入微任务队列(非立即执行的异步任务队列),并在当前宏任务完成后被执行。 3️⃣并发执行 -
误解
: 有时人们误以为多个 setTimeout 调用可以同时执行。
事实
: 虽然 setTimeout 可以并行设置多个定时器,但由于 JavaScript 的单线程特性,这些回调函数仍然会按顺序执行,不会同时运行。 4️⃣多次执行 -
误解
:一些开发者可能认为 setTimeout 会多次执行,特别是当他们试图在一个循环中设置多个定时器时。
事实
: 如果不适当地设置多个定时器,可能会导致意料之外的行为。例如,在循环中设置多个定时器而不考虑它们的执行顺序,可能导致所有回调几乎同时触发。
☕️setInterval
🎪 setInterval
函数作用和 setTimeout
基本一致,,区别在于setInterval
是每隔一段时间执行一次回调函数。
🚣 通常情况不是很推荐使用 setInterval
,原因有两点:他和 setTime
基本一致,不能保证在预期时间执行任务,其次就是存在执行积累的问题,比如说以下代码:
// 定义一个名为 demo 的函数
function demo() {// 使用 setInterval 设置一个定时器,每隔 1000 毫秒(1秒)执行一次提供的回调函数setInterval(function() {console.log(2); // 每次执行时打印数字 2 到控制台}, 1000); // 设置间隔时间为 1000 毫秒// 尝试调用 sleep 函数,等待 2000 毫秒(2秒)// 注意:JavaScript 中没有原生的 sleep 函数,这里是模拟休眠sleep(2000); // 这里的 sleep 函数在标准 JavaScript 中是不存在的// 由于 JavaScript 是单线程的,这里 sleep 函数如果是阻塞的,// 那么它将会阻止其他所有代码(包括定时器)的执行,直到它完成
}// 调用 demo 函数
demo();
🚅以上代码在浏览器环境中,如果定时器出现了耗时操作,由于 JavaScript 是单线程的,这里 sleep
函数如果是阻塞的,多个回调函数会在耗时操作结束以后同时执行,这样就可能会带来性能上的问题。
🎡 requestAnimationFrame
requestAnimationFrame
是一种在浏览器中用来执行动画的技术,它可以让动画更流畅且性能更高。
🚁 相比起上面两个函数它有以下优点:
- ✖️ 与屏幕刷新率同步:
-
requestAnimationFrame
的回调会在浏览器的下一帧绘制之前被调用,通常每秒 60 帧(即每 16.67 毫秒一帧),自带节流功能,这使得动画更加平滑。 ➕ 自动暂停: - 当包含动画的标签页不在前台时,
requestAnimationFrame
会自动暂停,从而节省计算资源。 ➖ 更高效: - 如果浏览器在某一帧内无法渲染(例如因为 CPU 负载过高),
requestAnimationFrame
不会堆积回调,而是跳过这一帧。
🌠requestAnimationFrame
函数的延时效果是精确的,没有其他定时器时间不准的问题,当然也可以通过该函数去实现 setTimeout
。
// 定义一个模拟 setInterval 的函数
function setInterval(callback, interval) {// 用于存储 requestAnimationFrame 的返回值let timer;// 获取当前时间的函数引用,提高代码可读性和一致性const now = Date.now;// 初始化开始时间和结束时间let startTime = now();let endTime = startTime;// 定义一个内部循环函数const loop = () => {// 使用 requestAnimationFrame 请求下一帧timer = window.requestAnimationFrame(loop);// 获取当前时间endTime = now();// 如果从开始到现在的时间差大于或等于指定的间隔时间 intervalif (endTime - startTime >= interval) {// 重置开始时间和结束时间startTime = endTime = now();// 调用回调函数,并传入当前的 timer 值callback(timer);}}// 第一次调用 loop 函数,开始循环timer = window.requestAnimationFrame(loop);// 返回 timer 值,以便将来可以通过它取消动画return timer;
}// 初始化计数器
let a = 0;// 使用自定义的 setInterval 函数,每隔 1000 毫秒(1秒)执行一次回调函数
setInterval(timer => {// 打印数字 1 到控制台console.log(1);// 计数器增加a++;// 如果计数器 a 达到 3,则取消动画if (a === 3) {cancelAnimationFrame(timer);}
}, 1000);
今天的分享就到这里啦,感谢大家的阅览,小江会一直与大家一起努力,文章中如有不足之处,你的支持是我前进的最大动力,请多多指教,感谢支持,持续更新中 ……