说说JavaScript定时器

下面代码的输出结果:

for(var i = 0; i < 10; i++) {
    setTimeout(function() {
        console.log(i);
    }, 1000);
}

答案是:10个10

由此,我们来了解一下JavaScript的定时机制。
众所周知,JavaScript引擎是单线程的,我们先来看一下JavaScript的线程图示:


说说JavaScript定时器_第1张图片
JavaScript线程图示

如图,一般浏览器内核中至少常驻三个线程:JavaScript引擎线程、GUI渲染线程、浏览器事件触发线程等。
其他线程产生的任务放到JavaScript引擎线程的队列中,然后一个接一个的执行。

//代码一
setTimeout(function() {
       console.log("123");
}, 1000);
//代码二
setTimeout(function() {
       console.log("abc");
        setTimeout(arguments.callee, 1000);
}, 1000);
//代码三
setInterval(function(){
        console.log("xyz");
}, 1000);

代码一,延迟执行,一秒后控制台打印出123
代码三,循环执行,每过一秒(<=1s)打印xyz
代码二,也是循环执行,每过一秒(>=1s)打印abc
由于arguments.callee不推荐使用了,所以代码二可以等价的写成:

setTimeout(function a() {
       console.log("abc");
        setTimeout(a, 1000);
}, 1000);

代码二和代码三是循环执行,会一直执行下去,容易造成浏览器假死,没反应,如果想要结束循环,可以通过如下方式:

//代码二改成这样,只执行10次
var num=0;
setTimeout(function a() {
       num++;
       console.log("abc");
       if(num<10){
               setTimeout(a, 1000);
       }
}, 1000);
//代码二也可以改成这样,执行11次
var num=0;
setTimeout(function a() {
       num++;
       console.log("abc");
       var timer=setTimeout(a, 1000);
       if(num>10){
               clearTimeout(timer);
       }
}, 1000);

同样的道理,代码三可以改成如下写法:

//9次
var num=0;
setInterval(function(){
        num++;
        if(num<10){
               console.log("xyz");
        }
}, 1000);
//11次
var num=0;
var i=setInterval(function(){
        num++;
        console.log("xyz");
        if(num>10){
                clearInterval(i);
        }
}, 1000);

差不多就这样吧,总结一下:

  • JavaScript是单线程的
  • JavaScript引擎是基于事件驱动的
  • 定时器产生的异步事件会插入到JavaScript引擎的事件队列中
  • JavaScript引擎执行完同步事件后会去继续执行事件队列中的异步事件,这就是为什么如下代码先打印0再打印1的原因
setTimeout(function(){
    console.log(1);
},0);
console.log(0);
  • 两个setTimeout构成的循环可以使用clearTimeout()去结束循环
  • setInterval循环可以使用clearInterval()去结束循环

参考阅读:
深入理解JavaScript定时机制
深入理解定时器系列第一篇——理解setTimeout和setInterval
浏览器UI多线程及对JavaScript单线程底层运行机制的理解

你可能感兴趣的:(说说JavaScript定时器)