JavaScript下的setTimeout(fn,0)的作用,涨知识了

单线程的javascript

首先我们来看浏览器下的JavaScript:

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

  • javascript引擎是基于事件驱动单线程执行的,JS引擎一直等待着任务队列中任务的到来,然后加以处理,浏览器无论什么时候都只有一个JS线程在运行JS程序。

  • GUI渲染线程负责渲染浏览器界面,当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行。但需要注意 GUI渲染线程与JS引擎是互斥的,当JS引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。

  • 事件触发线程,当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理。这些事件可来自JavaScript引擎当前执行的代码块如setTimeOut、也可来自浏览器内核的其他线程如鼠标点击、AJAX异步请求等,但由于JS的单线程关系所有这些事件都得排队等待JS引擎处理。(当线程中没有执行任何同步代码的前提下才会执行异步代码)

js是单线程的,浏览器有一个js任务队列,当在触发事件里面嵌套触发事件时,嵌套的这些事件会放在队列里面,等当前任务完成后再激活队列中的任务。解决方案就是使用setTimeout再次调整浏览器的代码任务运行队列。

看下面这2个对比的例子:

 <div>
       <h3>setTimeout</h3>
       <h4>1、未使用 <code>setTimeout</code></h4>
       <button id="generateInput">生成input</button>

        <p id="inpwrapper"></p>
   </div>

   <div>
       <h3>setTimeout</h3>
       <h4>2、使用 <code>setTimeout</code></h4>
       <button id="generateInput2">生成input</button>

       <p id="inpwrapper2"></p>
   </div>

   <div>


       <h3>3、另一个例子, 没有使用setTimeout()</h3>
       <p>
           <input type="text" id="input" value="aa" /><span id="preview"></span>
       </p>
   </div>
   <div>


       <h3>4、另一个例子, 使用setTimeout()</h3>
       <p>
           <input type="text" id="input2"  /><span id="preview2"></span>
       </p>
   </div>

 

<script>



     function get(id){
        return document.getElementById(id);
     }

      window.onload = function(){
      get("generateInput").onmousedown = function(){
         var input = document.createElement('input');
          input.setAttribute('type',"text");
          input.setAttribute('value',"test1");
         get("inpwrapper").appendChild(input);
          input.focus();
          input.select();

      }



        get("generateInput2").onmousedown = function(){
            var input = document.createElement('input');
            input.setAttribute('type',"text");
            input.setAttribute('value',"test1");
            get("inpwrapper2").appendChild(input);
            setTimeout(function(){
                input.focus();
                input.select();
            },0);


        };

        get('input').onkeypress = function(){
            get("preview").innerHTML = this.value;
        };
        var domInput =  get('input2');
        domInput.onkeypress = function(event){
            console.log(event);
            setTimeout(function(){
                console.log();
                get("preview2").innerHTML = domInput.value;
            },0);
        }
    }
</script>

基本效果展示:

未使用setTimeout(),测试新生成的input的focus事件是被丢弃了还是被触发了,加强版测试例子1:

   function get(id){
        return document.getElementById(id);
     }

      window.onload = function(){
          var gener1 =   get("generateInput");
          gener1.onmousedown = function(e){
              console.log(e.type);
         var input = document.createElement('input');
          input.setAttribute('type',"text");
          input.setAttribute('value',"test1");
         get("inpwrapper").appendChild(input);
          input.onfocus = function(){
              console.info('input focus');
          };
          input.focus();
          input.select();

      };
          gener1.onclick =function(e){
              console.log(e.type);
          };
          gener1.onmouseup =function(e){
              console.log(e.type);
          };
          gener1.onfocus =function(e){
              console.log("gener1 focus");
          };

  结果如下:

  例子1中,我们点击的按钮,在mousedown之后,才获得焦点,也就是说:我们的input本来已经得到了focus(),但在onmousedown之后,我们点击的按钮才迟迟触发了自己的onfocus(),导致我们的input的focus被覆盖。所以input才没有获得焦点。

未使用setTimeout(fn,0)之前,任务流程是这样的:

onmousedown->onmousedown中执行了input.focus=>button.onfoucs=>onmouseup=>onclick

使用setTimeout()后测试新生成的input的focus事件是被丢弃了还是被触发了,加强版测试例子1:

 var gener2 = get("generateInput2");
           gener2.onmousedown = function(e){
               console.log(e.type);
            var input = document.createElement('input');
            input.setAttribute('type',"text");
            input.setAttribute('value',"test1");
            get("inpwrapper2").appendChild(input);
               input.onfocus = function(){
                   console.info('input focus');
               };
            setTimeout(function(){
                input.focus();
                input.select();
            },0);


          };
          gener2.onclick =function(e){
              console.log(e.type);
          };
          gener2.onmouseup =function(e){
              console.log(e.type);
          };
          gener2.onfocus =function(e){
              console.log("gener1 focus");
          };

  运行结果如下:

示例2中的代码,我们延迟了焦点,当按钮获得焦点之后,我们的input再把焦点抢过来,所以,使用setTimeout(fn,0)之后,我们的input可以得到焦点并选中文本。

 使用了setTimeout之后任务流程是这样的:

onmouseup->button.onfocus=>input.focus=>onmouseup=>onclick

在工作学习中,我看到团队中的有些大神的代码中不时的出现setTimeout(fn,0);这样的写法,很好奇,这样写的作用是什么,在网上,经过一位网友无私耐心地阐述,我才对js单线程的机制有了更深的认识,此处我引上链接,与君共勉!

http://www.cnblogs.com/silin6/p/4333999.html

原文地址:https://www.cnblogs.com/lizimeme/p/6825765.html