网站首页 » 前端开发 » JavaScript » JavaScript 中的定时器执行机制大解密
上一篇:
下一篇:

JavaScript 中的定时器执行机制大解密

前言

JavaScript 中的定时器你一定用了不少,比如 setTimeout、setInterval 等。但我们对他的了解又有多少呢?我承认我以前一直用它只是因为他能为我延迟几秒执行某些操作,它确确实实为我解决了不少问题,还有一些比较“奇葩”(由于技术水平有限,未能找出原来)的问题也是通过定时器来解决的。但我慢慢地发现,我对定时器的了解也只是表面上的,而它的运行机制我却不闻不问,也就是我只看到了它成功的一面,却没有去研究它为什么会成功的征服了我。所以,我想花点时间来好好地跟“定时器”这位老朋友作一个深入的交流。

首先我们来看一段代码:

console.log(0); (1)

setTimeout(function(){ (2)
    console.log(1);
},1000);

console.log(2);(3)
// 执行结果 0 2 1

这是一段很简单的代码。但从执行结果中我们可以得出一些结论。

  • JS 是从上往下执行的
  • 定时器会在指定的时间后执行相应代码,会晚于其它代码执行,并且不会阻塞其它代码的执行。

那么现在问题来了,它的运行原理是什么呢?

定时器是异步的,所以不需等到它执行完返回结果,而是先把它挂起,继续往下执行其它代码。待时间到了后定时器线程会通知主线程,主线程收到通知后,就会执行回调函数。

setTimeout 一是一个发起函数(注册函数),用来发起异步操作。

setTimeout 中的匿名函数就是一个回调函数

setTimeout 中的时间(相当于触发事件)就是指什么时候把这个任务放到消息队列中。

当 setTimeout 中的时间到了后,就会把对应的回调函数封装成一个消息放到消息队列中,主线程会不断地从消息队列中取出消息(这里的 setTimeout 是其中一个)放到执行栈中执行。

let timeFlag = 'timePoint';
console.log('a');
 console.time(timeFlag);
 for (var i = 0;i<1000000000;i++) {}

 setTimeout(function(){
     console.log('b');
 },5);

 setTimeout(function(){
     console.log('c');
 },5);

 setTimeout(function(){
     console.log('d');
 },1000);
 console.log('e');
 console.timeEnd(timeFlag);
/*
 a
 e
 timePoint: 1666.347900390625ms
 b
 c
 d
*/

上面的过程大概如下:

先把同步代码执行完,当 JS 从上往下执行时,遇到了 setTimeout 就会把这个函数放到浏览器专门的定时器模块中,所有的同步代码都依次入栈出栈接着被主线程逐个处理,执行完后清空栈,然后到任务队列取出一个任务(在这里是上面代码中的三个定时器中最先被封装成任务的那个定时器的回调函数),放到执行栈中转为同步任务让主线程的来处理,在处理这个回调函数时发现它调用了console.log() 方法,于是又将 console.log() 方法入栈,然后对执行栈依次出栈执行,输出 b,清空执行栈。后面两个定时器同样道理。时间相同的定时器,就按照先后顺序放入任务队列。定时器模块是单独于主线程的,它有自己的线程来执行定时器的触发。为了说明这个定时器模块跟主线程是同时进行的,我们可以来看看下面这一段代码:

var start = new Date;(1)
console.time('timePoint');(2)
setTimeout(function(){ (4)
	console.timeEnd('timePoint');(6)
}, 500);(3)
while (new Date - start < 1000) {};(5)

// 最后打印出来的结果:1002.2626953125ms

上面的过程大致如下:

(1)->(2)->(3) ->(4) ->(5)->(6),执行到(4)时,setTimeout()方法入栈被执行,并把执行方法交给定时器模块来管理,继续往下执行其它代码(5),最后才会到(6)。只有当(6)执行完之后,才会调用定时器的回调函数,然后执行里面的 console.timeEnd(); 方法。结果为 1002.2626953125ms 接近 1s。说明代码(5)在执行的过程中(大约500ms的时候),定时器的回调函数被封装成任务放入任务队列中了。

setInterval 跟 setTimeout 的执行原理差不多,有时间的可以自己去好好体会下。

  • 微信扫一扫,赏我

  • 支付宝扫一扫,赏我

声明

原创文章,不经本站同意,不得以任何形式转载,如有不便,请多多包涵!

本文永久链接:http://yunkus.com/javascript-timer/

Leave a Reply

Your email address will not be published. Required fields are marked *

评论 END