Javascript定时器(三)——setTimeout(func, 0)

简介: setTimeout(func, 0)可以使用在很多地方,拆分循环、模拟事件捕获、页面渲染等

一、setTimeout中的delay参数为0,并不是指马上执行



<script type="text/javascript">
        function delay1() {
            console.log('delay1');
        }
        function delay2() {
            console.log('delay2');
        }
        function delay3() {
            console.log('delay3');
        }
        delay1();
        setTimeout(function() {
            delay2();
        }, 0);
        delay3();
</script>



  用firefox的firebug可以查看到,并不是按照delay1delay2delay3这样打印的。


  image.png


  由于JavaScript是单线程处理任务的,而setTimeout是异步事件,延时时间为0的时候,JavaScript引擎会把异步事件立刻放到任务队列里,而不是立刻执行,需要等到前面处于等待状态的事件处理程序全部执行完成后再调用它(JavaScript engines only have a single thread, forcing asynchronous events to queue waiting for execution)。JavaScript引擎的运作可以查看前面的文章《Javascript定时器(一)——单线程》

 

二、拆解循环



循环体中包含太多的操作和循环的次数过多都会导致循环执行时间过长,并直接导致锁死浏览器。如果循环之后没有其他操作,每次循环只处理一个数值,而且不依赖于上一次循环的结果则可以对循环进行拆解。



<script type="text/javascript">
  function delay() {
      for(var i=0; i<100000000; i++) {
                //logic
      }
    }
    delay();
</script>


使用setTimeout拆解循环,用firebug的console.log打印index值。


<script type="text/javascript">
        var index = 0;
        //使用setTimeout拆解循环
        function major() {
            if(index > 100000000) {
                return;
            }
            console.log(index);
            index++;
            setTimeout(function() {
                major();
            }, 0);
        }
        major();
</script>


三、模拟事件捕获


浏览器的 DOM 事件都是采用冒泡的方式,在实际的开发过程中可能存在需要事件捕获的需求,要求子元素的事件在父元素触发之后才能触发。


  <p>1、父元素的事件在子元素触发之后触发</p>
    <div id="parent">
        <a href="javascript:void(0)" id="child">冒泡</a>
        <p id="result1"></p>
    </div>

 1、一个a按钮

  2、一个p显示结果

  3、一个div作为父级元素


<script type="text/javascript">
        //父元素的事件在子元素触发之后触发
        function bubbling() {
            var child = document.getElementById('child'), 
                parent = document.getElementById('parent'),
                result = document.getElementById('result1');
            child.onclick = function() {
                result.innerHTML += 'child->';
            };
            parent.onclick = function() {
                result.innerHTML += 'parent->';
            };
        }
</script>


  1、绑定a按钮的click事件,点击一下打印child

  2、绑定div按钮的click事件,点击一下打印parent

  3、显示结果如下:

  image.png

在a按钮的click事件中加个setTimeout,就能做到子元素的事件在父元素触发之后触发了,这里也是JavaScript引擎在起作用。


    child.onclick = function() {
            setTimeout(function() {
                result.innerHTML += 'child->';
            }, 0);
       };


image.png

 


四、页面渲染


浏览器中GUI渲染线程与JavaScript引擎是互斥的,所以当JavaScript执行时,浏览器就不会做任何的页面渲染。


<p>3、页面渲染,阻塞</p>
<a href="javascript:void(0)" id="vary">变换</a>
<div style="width:150px;height:150px;border:1px solid #c6c6c6" id="container"></div>


  1、点击变换按钮,将下面container的宽与高从150减到1。

  2、由于互斥的关系,效果感觉是一下子从150到1了,中间没有那个减小的过程。


<script type="text/javascript">
     //页面渲染
        function render() {
            var btn = document.getElementById('vary'), container = document.getElementById('container');
            btn.onclick = function() {
              for (var i = 150; i > 0; i--) {
                container.style.height = i + 'px';
                container.style.width = i + 'px';
              }
            };
        }
</script>


  1、修改container的height与width会引发回流(reflow),一回流GUI渲染线程线程就会执行。

  2、由于互斥的原因,只有当JavaScript执行完毕后,才会做渲染。


  image.png=》image.png


用setTimeout后,就会出现从150到1的减小过程。



function vary(i) {
                i--;
                console.log(i);
                container.style.height = i + 'px';
                container.style.width = i + 'px';
                if(i > 0) {
                    setTimeout(function() {
                        vary(i);
                    }, 0);
                }
            }
            btn.onclick = function() {
                var i = 150;
                vary(i);
            };


Tips:

这里顺便说下重绘(Repaint)与回流(reflow):

重绘(repaints):是一个元素外观的改变所触发的浏览器行为,例如改变vidibility、outline、背景色等属性。浏览器会根据元素的新属性重新绘制,使元素呈现新的外观。重绘不会带来重新布局,并不一定伴随回流。

回流(reflow):是更明显的一种改变,可以理解为渲染树需要重新计算。

导致 reflow 发生的一些因素:

  1. 调整窗口大小(Resizing the window)
  2. 改变字体(Changing the font)
  3. 增加或者移除样式表(Adding or removing a stylesheet)
  4. 内容变化,比如用户在input框中输入文字(Content changes, such as a user typing text in an input box)
  5. 激活 CSS 伪类,比如 :hover (IE 中为兄弟结点伪类的激活)(Activation of CSS pseudo classes such as :hover (in IE the activation of the pseudo class of a sibling))
  6. 操作 class 属性(Manipulating the class attribute)
  7. 脚本操作 DOM(A script manipulating the DOM)
  8. 计算 offsetWidth 和 offsetHeight 属性(Calculating offsetWidth and offsetHeight)
  9. 设置 style 属性的值 (Setting a property of the style attribute)
相关文章
|
5月前
|
JavaScript 前端开发 安全
JavaScript基础-定时器:setTimeout, setInterval
【6月更文挑战第13天】JavaScript中的`setTimeout`和`setInterval`是异步编程的关键工具,用于按计划执行代码。`setTimeout`在延迟后执行一次,而`setInterval`则周期性执行。常见问题包括忘记清除定时器导致内存泄漏,递归使用`setTimeout`可能引发无限递归,以及字符串代码执行的安全隐患。解决方法包括使用`clearTimeout`和`clearInterval`,设置递归终止条件,以及优先使用函数表达式。理解定时器的非精确性并采用错误处理策略也是实践中的重要技巧。通过示例展示了如何延迟显示消息和周期性打印计数。
89 2
|
1月前
|
JavaScript 前端开发
js之一次性定时器
js之一次性定时器
11 1
|
1月前
|
JavaScript 前端开发
js之永久定时器
js之永久定时器
13 1
|
3月前
|
JavaScript 前端开发
解决在JS中阻止定时器“重复”开启问题、Vue中定时器的使用
这篇文章讨论了在JavaScript和Vue中如何避免定时器重复开启的问题,并提供了一个模拟场景和代码示例,演示了在开启新定时器前清除旧定时器的方法。
解决在JS中阻止定时器“重复”开启问题、Vue中定时器的使用
|
3月前
|
JavaScript 前端开发
js之永久定时器
js之永久定时器
|
3月前
|
JavaScript 前端开发
|
3月前
|
Web App开发 JavaScript 前端开发
JavaScript——定时器为什么是不精确的
JavaScript——定时器为什么是不精确的
64 0
|
3月前
|
JavaScript 前端开发
js之永久定时器
js之永久定时器
32 0
|
3月前
|
JavaScript 前端开发
js之一次性定时器
js之一次性定时器
30 0
|
4月前
|
JavaScript 前端开发 数据可视化
js 实现动画的两种方案对比:setTimeout vs RAF (requestAnimationFrame)
js 实现动画的两种方案对比:setTimeout vs RAF (requestAnimationFrame)
72 2