看高性能javascipt 这本书时,看到这么一句话:

Putting scripts at the top of the page in this way typically leads to a noticeable delay, often in the form of a blank white page, before the user can even begin reading or otherwise interacting with the page.

解释如下:

将script脚本放在头部将导致一个明显的延迟,通常的表现为:页面打开时一片空白,用户不能阅读也不能有任何交互。

我的理解是:将js放在头部,js的加载和执行导致页面渲染推迟。如果js放在尾部,由于页面渲染先于js的加载或执行,可以优先呈现给用户而不受js的阻塞影响。

为了验证,做了以下实验:

<p>hello world</p>
<script type="text/javascript">
  function f() {
    var t = +new Date();

    //运行5秒
    while(true) {
      if(+new Date() - t > 5000) {
        break;
      }
    }
  }
  f();  // ①
</script>

实验结果有意思了:hello world 等了5秒后才出来。

深度验证:将以上script里面部分作为js外链引入或是动态加载。结果依然是等待了5秒后才出来。

也就意味着页面的呈现必须是在所有js加载并执行完成后才会出现。上面那段话是有问题的。

这是否就意味着只要js不涉及到dom操作,放在头部和尾部是同样效果呢?当然不是。例如当html里面有图片或其他资源的时候,如果将js放在头部,图片的下载需要等待js运行完之后才开始,而如果js放入尾部,由于图片下载并不阻塞js的运行,可实现图片的下载和js运行的并行。

问题到此,是否就结束了呢?当然没有,前端工作很重要的一部分是性能优化,针对上述的js,如何进行优化呢?于是想到了setTimeout。

setTimeout 用处在于延迟执行js代码,它有个好处就是不会阻塞它后面js的执行。于是猜想setTimeout同样不会阻塞页面的渲染。

方案一:将上面的①部分替换为: setTimeout(f, 0);

结果hello world 还是要等待5秒后才出来。但是如果细心的话你会发现浏览器标签上的load符号不见了。

继续猜想,0太小了,导致浏览器发现 setTimeout 后面没有js代码后马上就执行了setTimeout 里面的内容,即 f 函数。于是把时间改成100ms。

方案二:将上面的①部分替换为:setTimeout(f, 100);

结果hello world瞬间弹出。有点小激动。有兴趣的同学可以继续测,这时你会发现会有一个临界值(不同浏览器的临界值不同),当setTimeout 第二个参数大于这个临界值时,hello world会瞬间弹出,反之则需要等待里面函数运行完成后弹出。

太神奇了,为什么会出现这种情况?要解答这个问题,我们必须要研究一下浏览器的线程机制了。

我们知道浏览器内部至少会有这么两个线程:解析js的线程和渲染界面的线程。这里我们暂且称它们为JS线程和UI线程。

由于js是可操纵DOM的,如果在修改这些元素属性同时渲染界面(即JS线程和UI线程同时运行),那么渲染线程前后获得的元素数据就可能不一致了。因此为了防止渲染出现不可预期的结果,浏览器控制JS线程和UI线程以列队的形式同步执行。

回到上面的问题,setTimeout执行时会新开一个定时器线程,这是正好处于JS线程运行当中,当JS线程执行完成后,发现setTimeout马上就要开始执行(即时间小于上述的临界值),为了避免UI线程运行时间太长而带来的setTimeout严重拖时的不好体验,浏览器选择一直等待直到setTimeout到期,然后运行里面的js。如果发现setTimeout还要隔较长时间才到期,为了避免时间上的浪费,浏览器选择马上切换到UI线程。

结论:setTimeout 可用于处理耗时的js代码,但千万要小心第二个参数不要设置太小了,否则你看到的同样是一个空白页面。推荐在100ms左右,可满足所有浏览器。当然,如果可以不兼容IE的话,抛弃setTimeout吧,web workers 会是个很好的选择。

从setTimeout到浏览器线程机制的更多相关文章

  1. JS线程机制与事件机制

    JS线程机制与事件机制 1.进程与线程 (1).定义: 进程:程序的一次执行,它占有一片独有的内存空间 CPU的基本调度单位,是程序执行的一个完整的流程 (2).进程与线程的关联 一个进程一般至少有一 ...

  2. Vue.js线程机制问题还是数据双向绑定有延迟的问题

    最近用select2做一个下拉多选,若只是从后端获取一个列表渲染还好说,没有任何问题.但要用select2对数据初始化时进行selected的默认选项进行显示,就出现问题了. vm.$set('are ...

  3. Java缓存学习之二:浏览器缓存机制

    浏览器端的九种缓存机制介绍 浏览器缓存是浏览器端保存数据用于快速读取或避免重复资源请求的优化机制,有效的缓存使用可以避免重复的网络请求和浏览器快速地读取本地数据,整体上加速网页展示给用户.浏览器端缓存 ...

  4. WebClient.DownloadFile(线程机制,异步下载文件)

    线程机制(避免卡屏),异步下载文件. 我做网站的监控,WebClient.DownloadFile这个方法是我经常用到的,必要的时候肯定是要从网上下载些什么(WebRequest 也可以下载网络文件, ...

  5. Java多线程与并发库高级应用-传统线程机制回顾

    1.传统线程机制的回顾 1.1创建线程的两种传统方式 在Thread子类覆盖的run方法中编写运行代码 // 1.使用子类,把代码放到子类的run()中运行 Thread thread = new T ...

  6. [转]浏览器渲染机制——一定要放在body底部的js引用

    转自:http://blog.csdn.net/u012251421/article/details/50536265 说明: 本文提到的浏览器均是指Chrome. “script标签“指的都是普通的 ...

  7. 详解浏览器缓存机制与Apache设置缓存

    一.详解浏览器缓存机制 对于,如何说明缓存机制,在网络上找到了两张图,个人认为思路是比较清晰的.总结时,上图. 这里需要注意的有两点: 1.Last-Modified.Etag是响应头里的数据 2.I ...

  8. atitit。浏览器缓存机制 and 微信浏览器防止缓存的设计 attilax 总结

    atitit.浏览器缓存机制 and 微信浏览器防止缓存的设计 attilax 总结 1. 缓存的一些机制 1 1.1. http 304 1 1.2. 浏览器刷新的处理机制 1 1.3. Expir ...

  9. java中线程机制

    java中线程机制,一开始我们都用的单线程.现在接触到多线程了. 多线性首先要解决的问题是:创建线程,怎么创建线程的问题: 1.线程的创建: 四种常用的实现方法 1.继承Thread. Thread是 ...

随机推荐

  1. ASP.NET MVC 4.0 学习3-Model

    Model負責獲取數據庫中的資料,並對數據庫中的數據進行處理. MVC中有關 數據庫 的任務都由Model來完成,Model中對數據資料進行定義,Controller和View中都會參考到Model, ...

  2. BZOJ 3529 数表(莫比乌斯反演)

    http://www.lydsy.com/JudgeOnline/problem.php?id=3529 思路:令F(i)为i的约数和, 1<=x<=n,1<=y<=m G(i ...

  3. TCP Keepalive HOWTO

    TCP Keepalive HOWTO Fabio Busatto <fabio.busatto@sikurezza.org> 2007-05-04 Revision History Re ...

  4. cf479E Riding in a Lift

    E. Riding in a Lift time limit per test 2 seconds memory limit per test 256 megabytes input standard ...

  5. python选择排序实现与C选择排序实现

    python代码: #coding=utf-8 if __name__=="__main__": arr=[3,2,1,7,11,4,5,8] print "Before ...

  6. redhat6.3 64位更新源(使用网易源)全过程记录

    本篇博客参考:http://chinaxiaoyu.diandian.com/post/2013-01-24/40046529897.首先在浏览器中输入http://tel.mirrors.163.c ...

  7. Unity 图片分割将spirte保存在本地

    如果你拿到的是一张整图,你想分割之后使用NGUI sprite来使用!  下面就能解决的需求. 步骤: 1. 使用Unity自带的spirte进行分割图片 2. 使用代码把分割出来的2DSpirte转 ...

  8. 浏览器缓存相关http头

    近期看雅虎黄金34条,学习下优化站点性能的方法. 当中有一条:"为文件头指定Expires或Cache-Control",详细来说指对于静态内容:设置文件头过期时间Expires的 ...

  9. 使用disqus搭建comment时一件非常二的事

    近期在github 上面搭建自己的博客,搭建comment部分的时候出现了一个问题:配置都配置好了,可是comment就是不成功.昨天为这个问题折腾了了半晚上没找出原因,今天晚上我突然发现一个地方设置 ...

  10. mvc 日历控件

    第二个是日历控件,在网上查了一个普通的日历控件,也生成了下拉的日历样子,但是一些脚本比如选择年月,需要一些时间,最后只好套用了My97 DatePicker,这样以来其实简单多了. 第一步:下载 My ...