跳到主要内容

setTimeout的理解

setTimeout到底是同步还是异步的 ?​

定时器会在当前任务队列执行完之后才会执行定时器的回调

需要注意的是,setTimeout()只是将事件插入了"任务队列",必须把当前任务队列执行完之后才会执行定时器的回调。要是当前任务队列耗时很长,有可能要等很久,所以并没有办法保证,回调函数一定会在setTimeout()指定的时间执行。

综上所属 setTimeout是单线程,类似异步,但不是异步 。

  • 异步的三种实现方式
    • 回调函数, 回调函数不一定是异步 , 但异步一定有回调函数
    • 事件
    • promise对象

setTimeout时间设置为0的理解​

text
setTimeout(function(){
console.log(1);
}, 0);
console.log(2);
console.log(3);
  • 得到的结果竟然是2、3、1

为啥呢?

  • js是单线程的,单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着
  • 当一个事件被触发时该线程会把事件添加到待处理任务队列的队尾
  • 当js代码执行遇到 setTimeout(fn,millisec)时,会把fn这个函数放在任务队列中,会把fn这个函数放在任务队列中,当js引擎线程空闲时并达到millisec指定的时间时,才会把fn放到js引擎主线程中执行。
  • setTimeout(fn,0)的含义是,指定某个任务在主线程最早可得的空闲时间执行,也就是说,尽可能早得执行。它在"任务队列"的尾部添加一个事件,因此要等到同步任务和"任务队列"现有的事件都处理完,才会得到执行。

setTimtout定时器的时间一定准吗?​

  • 不一定准,因为js是单线程的,定时器会在当前任务队列执行完之后才会执行定时器的回调
  • 假如当前队列任务执行时间大于定时器设置的延迟时间,那么定时器就不是那么可靠

定时器的第三个参数列表​

  • 第三个参数是可选,可添加多个参数, 用来传递给回调函数的参数
  • 第三个参数还可以是先执行,然后再执行回调函数

为什么setTimeout会有4ms的延时?​

  • 按照 W3C 的标准,浏览器实现计时器时,如果嵌套层级超过5层,则会带有4毫秒的最少时间

如何实现一个setTimeout?/如何实现准时的setTimeout?​

  • 1、requestAnimationFrame
  • 2、performance.now()
    • 通过时间调整间隔偏差
  • 3、webworker 【推荐】
    • 通过新开一个新的后台线程,去完成计时操作,不受主线程的影响
    • 可封装一个通用的webworker来实现定时器

requestAnimationFrame与setTimeout相比,requestAnimationFrame 最大的优势是由浏览器来决定回调函数的执行时机

  • performance.now()返回当前时间(以毫秒为单位),因此可以跟踪从调用setTimeoutRAF开始到当前时间的时间间隔
  • 如果时间间隔大于给定的延迟,则会调用回调函数,否则会再次调用requestAnimationFrame

1、注意: requestAnimationFrame的执行频率取决于浏览器的刷新率,并且可能会受到其他因素的影响

2、performance.now() 与页面加载有关,Date.now() 依赖于系统的时间

js
function setTimeoutRAF(callback, delay) {
let start = performance.now();

function timeout() {
let elapsed = performance.now() - start;

if (elapsed >= delay) {
callback();
} else {
requestAnimationFrame(timeout);
}
}

requestAnimationFrame(timeout);
}

例如,要在2秒后调用回调函数,可以这样调用setTimeoutRAF:

js
Copy codesetTimeoutRAF(() => {
console.log('2 seconds have passed');
}, 2000);

setTimeout时间设置为0的理解​

text
setTimeout(function(){
console.log(1);
}, 0);
console.log(2);
console.log(3);
  • 得到的结果竟然是2、3、1

为啥呢?

  • js是单线程的,单线程就意味着,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着
  • 当一个事件被触发时该线程会把事件添加到待处理任务队列的队尾
  • 当js代码执行遇到 setTimeout(fn,millisec)时,会把fn这个函数放在任务队列中,会把fn这个函数放在任务队列中,当js引擎线程空闲时并达到millisec指定的时间时,才会把fn放到js引擎主线程中执行。
  • setTimeout(fn,0)的含义是,指定某个任务在主线程最早可得的空闲时间执行,也就是说,尽可能早得执行。它在"任务队列"的尾部添加一个事件,因此要等到同步任务和"任务队列"现有的事件都处理完,才会得到执行。

定时器中的函数调用需要注意的地方​

  • 方式一:

    函数不加括号:函数名称作为函数的指针,用于传参,此时不是得到函数的结果,因为不会运行函数体代码。它只是传递了函数体所在的地址位置,在需要的时候好找到函数体去执行。

    • 不带括号的是绑定事件,事件触发再执行

    函数加了括号:带括号的是函数调用,直接执行函数,返回函数结果