• 从setTimeout到浏览器线程机制


    看高性能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 会是个很好的选择。

  • 相关阅读:
    如何正确的学习?
    GitHub的使用
    freemarker
    Vue的前端路由
    推荐一个压缩图片好用的网站-tinyPNG
    纯CSS实现滚动彩虹色文字
    npm镜像源管理
    UI、UE和UX三者之间的区别?
    移动端rem布局的学习(基于一个网易云播放页面的思考)
    移动前端不得不了解的html5 head 头标签
  • 原文地址:https://www.cnblogs.com/beyondcheng/p/3440154.html
Copyright © 2020-2023  润新知