五分钟面试题——requestAnimationFrame 和 setTimeout 区别

前言

对于现在技术社区的现状的,我分析用户画像 觉得大部分的用户更多的是喜欢短频快的节奏文章,所以故尝试用最短时间挖掘简单的面试题,将背后的知识整理分享给大家,喜欢在后续的面试过程中,可以对你有帮助,如果刚好是自己的知识盲区不妨帮忙点赞支持一下

背景

相信大部分的开发者都使用过 setTimeout 这个 API, 如果没有使用过可以,使用跳转地址setTimeout 但是我相信很多开发者是没有使用过requestAnimationFrame 这个 API的,因为我们平时开发中几乎不需要用到,相信也有朋友在 React

下面我们分开介绍两个API,在正式开始之前我们不妨先学习一下如何使用 requestAnimationFrame

requestAnimationFrame

顾明思议,RequestAnimationFrame, 在动画帧请求做件事情,具体可以看科普文章

window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行

语法

window.requestAnimationFrame(callback);
复制代码

用法

一般用在操作动画帧上,在物体需要在一帧渲染之前执行某个动画。一般是更好的优化动画上

const element = document.getElementById('some-element-you-want-to-animate'); // 需要移动的物体元素

let start;

function step(timestamp) {
  if (start === undefined) start = timestamp;
    
  const elapsed = timestamp - start;

  //这里使用`Math.min()`确保元素刚好停在200px的位置。
  element.style.transform = 'translateX(' + Math.min(0.1 * elapsed, 200) + 'px)'; // 每次移动 0.1 * elapsed 时间

  if (elapsed < 2000) { // 在两秒后停止动画
    window.requestAnimationFrame(step); 
  }
}

window.requestAnimationFrame(step); // 执行动画
复制代码

返回值

你没看错 requestAnimationFrame 是有返回值的。

一个 long 整数,请求 ID ,是回调列表中唯一的标识。是个非零值,没别的意义。你可以传这个值给 window.cancelAnimationFrame() 以取消回调函数。

cancelAnimationFrame 调用参数是先前调用 window.requestAnimationFrame()方法时候返回的ID

setTimeout

下面介绍的 setTimeout 相信大部分的开发者都使用过这个API吧,而且可以说相当熟悉

语法

var timeoutID = scope.setTimeout(function[ , delay, arg1, arg2, ...]);
var timeoutID = scope.setTimeout(function[, delay]);
var timeoutID = scope.setTimeout(code[, delay]);
复制代码

用法

用于延迟执行某个动作,或者可以循环调用自身,实现 setInterval 的效果,但注意需要释放 setTimeout 返回的 ID 利用clearTimeout 清空定时器。

var timeoutID;

function delayedAlert() {
  timeoutID = window.setTimeout(slowAlert, 2000);
}

function slowAlert() {
  alert('That was really slow!');
}

function clearAlert() {
  window.clearTimeout(timeoutID);
}
复制代码

返回值

返回值timeoutID是一个正整数,表示定时器的编号。这个值可以传递给clearTimeout()来取消该定时器。

这里有意思的点是 setTimeout()setInterval()共用一个编号池,技术上,clearTimeout()和 clearInterval() 可以互换。但是,为了避免混淆,不要混用取消定时函数。两者竟然是可以互换的

在同一个对象上(一个window或者worker),setTimeout()或者setInterval()在后续的调用不会重用同一个定时器编号。但是不同的对象使用独立的编号池。

两者对比

下面我们总结一下他们的差异

requestAnimationFrame setTimeout
语法 window.requestAnimationFrame(callback); setTimeout(callback, time)
用法 用于动画帧渲染时候,优化动画渲染场景中 用于延迟执行某个动作的场景中
返回值 正整数(用于后续 cancelAnimationFrame) 正整数 (用于后续 clearTimeout)
调用周期 0.2 ~ 0.3 ms (大约1帧的时间) 1 ~ 3 ms (setTimeout 第二个参数传入 0 情况下)

所以两者其实在时间上来理解的话,是在不同的调用周期中执行,setTimeout 是没有办法在 0.2 ~ 0.3 ms范围内执行某个回调的,这里我们提到时间范围的概念,这里就不得不提到 setTimeout 最小延迟时间的概念

setTimeout 最小延迟时间

console.time('ok')
setTimeout(() => {console.timeEnd('ok')}, 0); // ok: 1.93115234375 ms 预期是 0ms 实际上 大于 1ms
复制代码

有很多因素会导致setTimeout的回调函数执行比设定的预期值更久。

首先是浏览器之间的差异, 在浏览器中,setTimeout()/setInterval() 的每调用一次定时器的最小间隔是4ms,这通常是由于函数嵌套导致(嵌套层级达到一定深度),或者是由于已经执行的setInterval的回调函数阻塞导致的。例如:

function cb() { f(); setTimeout(cb, 0); }
setTimeout(cb, 0);
复制代码
setInterval(f, 0);
复制代码

在Chrome 和 Firefox中, 定时器的第5次调用被阻塞了;在Safari是在第6次;Edge是在第3次。Gecko 从这个版本 version 56开始对 setInterval() 开始采用这样的机制(setTimeout()已经实现,具体请参考以下内容)。

一直以来,不同浏览器中出现这种最小延迟的情况有所不同(例如Firefox) - 从其他地方调用了setInterval( ),或者在嵌套函数调用setTimeout( ) 时(嵌套级别达到特定深度时),都会出现超时延迟。

那么浏览器中实现0ms延时的定时器,这里就需要用 window.postMessage ,这个并不在本次内容介绍中说明.

小结

这篇文章到这里就结束了,水平有限难免有纰漏,欢迎纠错。最后希望帮忙点点赞,这对我创作是无比的肯定和动力。希望可以帮到你

文章参考

www.paulirish.com/2011/reques…
hacks.mozilla.org/2011/08/ani…
developer.mozilla.org/zh-CN/docs/…

猜你喜欢

转载自juejin.im/post/7078941440989986829