细说setTimeout

作者: 叫我徐小星 | 来源:发表于2017-01-19 10:55 被阅读134次

    setTimeout通常用于在指定的时间之后调用函数或表达式,多用作计时器。用法如下:

    setTimeout(code, millisec, args);
    

    code:要调用的函数或表达式
    millisec:指定的时间间隔
    args:非必须,若code为函数,args可为传入的函数所需的参数

    setTimeout(function(a, b){   
      console.log(a + b);
    },0, 1, 2);//3
    

    释:setTimeout(fn, millisec)表示立即执行。

    1.setTimeout执行原理

    浏览器的内核是多线程的,它们在内核控制下相互配合以保持同步,一个浏览器至少实现三个常驻线程:JavaScript引擎线程,GUI渲染线程,浏览器事件触发线程。

    • JavaScript引擎是基于事件驱动单线程执行的,JavaScript引擎一直等待着任务队列中任务的到来,然后加以处理,浏览器无论什么时候都只有一个JavaScript线程在运行JavaScript程序。
    • GUI渲染线程负责渲染浏览器界面,当界面需要重绘(Repaint)或由于某种操作引发回流(Reflow)时,该线程就会执行。但需要注意,GUI渲染线程与JavaScript引擎是互斥的,当JavaScript引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JavaScript引擎空闲时立即被执行。
    • 事件触发线程,当一个事件被触发时,该线程会把事件添加到待处理队列的队尾,等待JavaScript引擎的处理。这些事件可来自JavaScript引擎当前执行的代码块如setTimeout、也可来自浏览器内核的其他线程如鼠标点击、Ajax异步请求等,但由于JavaScript的单线程关系,所有这些事件都得排队等待JavaScript引擎处理(当线程中没有执行任何同步代码的前提下才会执行异步代码)。
    var start = new Date();  
     
    var end = 0;  
     
    setTimeout(function() {   
     
      console.log(new Date() - start);  
     
    },  500);  
     
    while (new Date() - start <= 1000) {}   //1002
    

    释:虽然setTimeout的延时时间是500毫秒,可是由于while循环的存在,只有当间隔时间大于1000毫秒时,才会跳出while循环,也就是说,在1000毫秒之前,while循环都在占据着JavaScript线程。也就是说,只有等待跳出while后,线程才会空闲下来,才会去执行之前定义的setTimeout。

    结论:setTimeout只能保证在指定的时间后将任务(需要执行的函数)插入任务队列中等候,但是不保证这个任务在什么时候执行。一旦执行javascript的线程空闲出来,自行从队列中取出任务然后执行它。

    1.setTimeout与this

    看下面一段代码:

    var a = 1;   
    var obj = {   
      a: 2,   
      test: function() { 
        setTimeout(function(){   
          console.log('a');   
        }, 0); 
        console.log(this.a)  ;  
      }   
    };   
    obj.test();
    

    我们知道上面一段的执行结果为:

    2
    'a'
    

    现将函数里面打印的‘a’换成this.a,如下:

    var a = 1;   
    var obj = {   
      a: 2,   
      test: function() { 
        setTimeout(function(){   
          console.log(this.a);   
        }, 0); 
        console.log(this.a)  ;  
      }   
    };   
    obj.test();
    

    执行结果为:

    2
    1
    

    若将全局变量a不赋值,代码如下:

    var a ;   
    var obj = {   
      a: 2,   
      test: function() { 
        setTimeout(function(){   
          console.log(this.a);   
        }, 0); 
        console.log(this.a)  ;  
      }   
    };   
    obj.test();
    

    其执行结果为:

    2
    undefined
    

    由此可知,setTimeout里回调函数的this是指向window的。
    注:
    1.setTimeout方法是在window对象下的,完整的写法是window.setTimeout;
    2.超时调用的函数都是在全局作用域中执行的。

    那么如何改变setTimeout回调函数里的this指向呢?目前有两种方法。
    1.使用bind():

    var a = 1;   
    var obj = {   
      a: 2,   
      test: function() {   
        setTimeout(function(){   
          console.log(this.a);   
        }.bind(this), 0);   
      }   
    };   
    obj.test(); //2
    

    2.将this赋给一个变量:

    var a = 1;   
    var obj = {   
      a: 2,   
      test: function() {   
        var _this = this;
        setTimeout(function(){   
          console.log(_this.a);   
        }, 0);   
      }   
    };   
    obj.test(); //2
    

    相关文章

      网友评论

        本文标题:细说setTimeout

        本文链接:https://www.haomeiwen.com/subject/xrfibttx.html