在聊setTimeout和setInterval这两个事件的前,先聊另外一个与之密切关联的知识点,那就是线程(thread)。而线程有常常跟另外一个词语--“进程”一起出现。那么何为线程?何为线程呢?关于线程,维基百科上是这么解释的:Thread (computing), a sequence of instructions that may execute in parallel with others(可能与其他指令并行执行的指令序列)。而进程是这么解释的:Process (computing), a computer program, or running a program concurrently with other programs(一种计算机程序,或与其他程序同时运行一个程序)。看起来有点绕,那么举一个形象点的例子,比如说打开电脑桌面的QQ,那么这个就是一个进程,而你可以在QQ上同时跟不同的人聊天、发离线文件、 发照片,那么这一个个操作就是线程。“与其他程序同时运行一个程序”,前面一个程序指的就是线程,而后面一个程序指的才是进程。
在js解析器中,js的执行是单线程的,也就是说一个进程执行过程中只能执行一个操作,不能同时执行几个操作。这个与其他后端的语言,如java,pyhton,甚至是js写的node.js有较大区别,这跟主要处理的业务对象是有关系的。另外一点,我们还需要区分浏览器的线程问题,浏览器是多线程的,除了js引擎线程,它还同时执行其他操作,例如:UI渲染线程,http请求线程,EventLoop轮询的处理线程等等。
讲了一大堆线程和进程,那么js究竟是如何处理setTimeout和setInterval的呢?
先举两个例子:
例子一:
setTimeout( function colorTurn(){ box1.style.backgroundColor="yellow"; },3000); box1.style.backgroundColor="blue";
在浏览器上:盒子先显示蓝色,3秒后显示黄色
例子二:
setInterval( function colorTurn(){ box2.style.backgroundColor="yellow"; },3000);
box2.style.backgroundColor="blue";
在浏览器上:盒子先显示蓝色,3秒后显示黄色
结果与例子一是一样的,但实际上,setInterval与setTimeout是有很大区别的,setTimeout只执行一次,而setInterval以函数内的时间参数为间隔不断执行,直到用clearInterval清除。可以用循环回调函数实现加法来验证setInterval,我在这里就不写出来了。
例一抽象出来的函数定义:
function process1(){
//do something}
setTimeout(fucntion process2(){
//do something
},3000)
function process3(){
//do something}
例二抽象出来的函数定义:
function process1(){
//do something}
setInterval(fucntion process2(){
//do something
},3000)
function process3(){
//do something}
这两个函数都涉及到了我上面谈到的线程问题,setTimeout是使函数延迟一定时间后再执行,setInterval是使函数以一定时间间隔执行。
当js执行setTimeout函数时,会延迟一定的时间才执行。例一中,函数执行的顺序为process1-process3-process3。由于js解析器是单线程的,所以必须是一条命令执行完后才到下一条命令,所以process2必须等Process3甚至后面的process4执行完了,3秒钟时间间隔到了它才会去检查线程,如果空闲,它会马上执行,如果有程序在执行,那么它会等当前程序执行完之后再执行。那么,如果process3执行时间超过了3秒呢?这种情况下,process2会一直等待,直到process3执行完毕它才执行。
同理,setInterval函数也是如此。不同的是,它会每隔一定时间,在这里是3秒钟检查一下线程队列,如果没有程序(函数)在执行,那么它执行,如果有程序在执行,那么它会继续等待,直到下一次的3秒之后。
再深入一点:
如果是setTimeout(或setInterval)里面嵌套setTimeout(或者setInterval)呢?那么还是那个原理,函数按序进入执行堆栈,必须是一段程序执行完后才会执行下一段程序。嵌套在里面的setTimeout会在上级setTimeout基础上继续等待一段时间,当等待时间到后,检查线程队列,如果空闲,就执行,如果有程序在执行,就继续等待。执行的顺序是外面的setTimeout会先于里面的setTimeout。
另外有一点值得了解一下:
setTimeout和setInterval都是window的函数,所以setTimeout和setInerval也可以写成window.setTimeout和window.setInterval。平时为了方便,往往省去了window而已。