同步加载的问题

  默认的js是同步加载的,这里的“加载”可以理解成是解析、执行,而不是“下载”,在最新版本的浏览器中,浏览器对于代码请求的资源都是瀑布式的加载,而不是阻塞式的,但是js的执行总是阻塞的。这会引起什么问题呢?如果我的index页面要加载一些js,但是其中的某个请求迟迟得不到响应,于是阻塞了后面的js代码的执行(同步加载),同时页面渲染也不能继续(如果js引入是在head标签后)。

<script type="text/javascript" src='http://china-addthis.googlecode.com/svn/trunk/addthis.js'></script>
<script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
this is a test

  比如上面的这段代码,保存为index.html文件,页面的主体是一个简单的字符串,但是代码执行后页面迟迟都是空白,为何?因为请求的js迟迟无法加载(可能由于谷歌被墙等原因),于是阻塞了后面的代码的执行,页面得不到渲染。可能你会提议,把js代码放到</body>前不就能先渲染页面了!好方法,我们尝试着将js放后面:

this is a test
<script type="text/javascript" src='http://china-addthis.googlecode.com/svn/trunk/addthis.js'></script>
<script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>

  页面瞬间被渲染,“this is a test"也很快出现在前台,世界似乎平静了,可是:

this is a test
<script type="text/javascript" src='http://china-addthis.googlecode.com/svn/trunk/addthis.js'></script>
<script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
<script type="text/javascript">
  console.log('hello world');
</script>

  在前面代码的基础上简单加了一段代码,但是"hello world"迟迟无法在控制台输出,显然前面的js请求阻塞了后面代码的加载,我们恍然大悟,改变js的加载位置只能改变页面的渲染,然而对于js的加载并没有什么卵用,js还是会阻塞。

实现js异步加载

  我们的要求似乎很简单,能在页面加载的同时,在控制台输出字符串即可,再讲的通俗一点,就是在请求第一段谷歌提供的js的同时,继续执行下面的js,也就是实现js的异步加载。

  最常见的做法是动态生成script标签:

<body>
  this is a test
  <script type="text/javascript">
    ~function() {
      var s = document.createElement('script');
      s.src = 'http://china-addthis.googlecode.com/svn/trunk/addthis.js';
      document.body.appendChild(s);
    }();
  </script>
  <script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
  <script type="text/javascript">
    console.log('hello world');
  </script>
</body>

  但是还是有点问题,这种加载方式在加载执行完之前会阻止 onload 事件的触发,而现在很多页面的代码都在 onload 时还要执行额外的渲染工作等,所以还是会阻塞部分页面的初始化处理:

<body>
  this is a test
  <script type="text/javascript">
    ~function() {
      // function async_load() {
        var s = document.createElement('script');
        s.src = 'http://china-addthis.googlecode.com/svn/trunk/addthis.js';
        document.body.appendChild(s);
      // }
      // window.addEventListener('load', async_load, false);
    }();

    window.onload = function() {
      var txt = document.createTextNode(' hello world');
      document.body.appendChild(txt);
    };
  </script>
  <script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
</body>

  比如上面的代码不能很好地渲染”hello world”,我们只需将注释去掉就可以了,让谷歌提供的js在onload 时才开始异步加载。这样就解决了阻塞 onload 事件触发的问题。

  补充DOMContentLoaded 与 OnLoad 事件   DOMContentLoaded : 页面(document)已经解析完成,页面中的dom元素已经可用。但是页面中引用的图片、subframe可能还没有加载完。 OnLoad:页面的所有资源都加载完毕(包括图片)。浏览器的载入进度在这时才停止。这两个时间点将页面加载的timeline分成了三个阶段。

  以上似乎能较好解决这个问题,但是html5提供了更简便的方法,async属性!

this is a test
<script type="text/javascript" src='http://china-addthis.googlecode.com/svn/trunk/addthis.js' async='async'></script>
<script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
<script type="text/javascript">
  console.log('hello world');
</script>

  async是html5的新属性,async 属性规定一旦脚本可用,则会异步执行(一旦下载完毕就会立刻执行)。

  需要注意的是async 属性仅适用于外部脚本(只有在使用 src 属性时)

  defer属性常常和async一起提起:

this is a test
<script type="text/javascript" src='http://china-addthis.googlecode.com/svn/trunk/addthis.js' defer='defer'></script>
<script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
<script type="text/javascript">
  console.log('hello world');
</script>

  似乎实现效果差不多,但是真的一样吗?我们来看看defer属性的定义。

  以前的defer只支持ie的hack,现在html5的出现开始全面支持defer。defer 属性规定当页面已完成加载后,才会执行脚本。defer 属性仅适用于外部脚本(只有在使用 src 属性时)。ps:ie支持的defer似乎并非如此,因为对ie无感,不深究,有兴趣的可以去查阅相关资料。

  既然async和defer经常一起出现,那么辨析一下吧!

  如果没有async和defer属性(赋值为true,下同),那么浏览器会立即执行当前的js脚本,阻塞后面的脚本;如果有async属性,加载和渲染后续文档元素的过程将和当前js的加载与执行并行进行(异步);如果有defer属性,那么加载后续文档元素的过程将和 script.js 的加载并行进行(异步),但是 script.js 的执行要在所有元素(DOM)解析完成之后,DOMContentLoaded 事件触发之前完成。

  来看一张网上盗的图:

  蓝色线代表网络读取,红色线代表执行时间,这俩都是针对脚本的;绿色线代表 HTML 解析。

  此图告诉我们以下几个要点(摘自defer和async的区别):

  1. defer 和 async 在网络读取(下载)这块儿是一样的,都是异步的(相较于 HTML 解析)
  2. 它俩的差别在于脚本下载完之后何时执行,显然 defer 是最接近我们对于应用脚本加载和执行的要求的
  3. 关于 defer,此图未尽之处在于它是按照加载顺序执行脚本的,这一点要善加利用
  4. async 则是一个乱序执行的主,反正对它来说脚本的加载和执行是紧紧挨着的,所以不管你声明的顺序如何,只要它加载完了就会立刻执行
  5. 仔细想想,async 对于应用脚本的用处不大,因为它完全不考虑依赖(哪怕是最低级的顺序执行),不过它对于那些可以不依赖任何脚本或不被任何脚本依赖的脚本来说却是非常合适的,最典型的例子:Google Analytics

  

  但是在我看来(以下个人理解,如有出入还望指出),defer在异步加载上的应用并不会比async广。async的英文解释是异步,该属性作用在脚本上,使得脚本加载(下载)完后随即开始执行,和动态插入script标签作用类似(async只支持h5,后者能兼容浏览器);而defer的英文解释是延迟,作用也和字面解释类似,延迟脚本的执行,使得dom元素加载完后才开始有序执行脚本,因为有序,所以会带来另一个问题:

this is a test
<script type="text/javascript" src='http://china-addthis.googlecode.com/svn/trunk/addthis.js' defer='defer'></script>
<script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js' defer='defer'></script>
<script type="text/javascript" src='index.js' defer='defer'></script>
console.log('hello world');

  如果执行这段代码,控制台的“hello world”也会迟迟得不到结果。所以我觉得还是async好用,如果要考虑依赖的话,可以选择requirejs、seajs等模块加载器。

总结

  JavaScript的异步加载还有一些方式,比如:AJAX eval(使用AJAX得到脚本内容,然后通过eval(xmlhttp.responseText)来运行脚本)、iframe方式等。

  以上理解如果有出入,还望指出~

  

不得不说的JavaScript异步加载的更多相关文章

  1. Javascript 异步加载详解

    Javascript 异步加载详解 本文总结一下浏览器在 javascript 的加载方式. 关键词:异步加载(async loading),延迟加载(lazy loading),延迟执行(lazy ...

  2. Javascript 异步加载详解(转)

    本文总结一下浏览器在 javascript 的加载方式. 关键词:异步加载(async loading),延迟加载(lazy loading),延迟执行(lazy execution),async 属 ...

  3. javascript异步加载的三种解决方案

    默认情况javascript是同步加载的,也就是javascript的加载时阻塞的,后面的元素要等待javascript加载完毕后才能进行再加载,对于一些意义不是很大的javascript,如果放在页 ...

  4. javascript异步加载详解(转)

    本文总结一下浏览器在 javascript 的加载方式. 关键词:异步加载(async loading),延迟加载(lazy loading),延迟执行(lazy execution),async 属 ...

  5. JavaScript异步加载的三种方式——async和defer、动态创建script

    一.script标签的位置 传统的做法是:所有script元素都放在head元素中,必须等到全部js代码都被下载.解析.执行完毕后,才能开始呈现网页的内容(浏览器在遇到<body>标签时才 ...

  6. JavaScript异步加载方案

    (1) defer,只支持IE defer属性的定义和用法(我摘自w3school网站) defer 属性规定是否对脚本执行进行延迟,直到页面加载为止. 有的 javascript 脚本 docume ...

  7. Python+Selenium+PhantomJS下载JavaScript异步加载网页

    # -*- coding: utf-8 -*- from selenium import webdriver import selenium.webdriver.support.ui as ui fr ...

  8. ztree异步加载

    Ztree异步加载的意思就是: 当点击展开树节点时,才去请求后台action返回点击节点的子节点数据并加载. 直接贴代码(SpringMvc+Mybatis): 前台页面ztreeList.jsp: ...

  9. 谈谈异步加载JavaScript

    前言 关于JavaScript脚本加载的问题,相信大家碰到很多.主要在几个点—— 1> 同步脚本和异步脚本带来的文件加载.文件依赖及执行顺序问题 2> 同步脚本和异步脚本带来的性能优化问题 ...

随机推荐

  1. Java主要有那几种文件类型?各自的作用是什么

    1.源代码.java程序员编译的源代码. 2..class  字节码文件 jvm实现跨平台的中间编译文件. 3.jar包 讲字节码文件打包好,便于查找和使用.

  2. 第一篇:微信公众平台开发实战Java版之了解微信公众平台基础知识以及资料准备

    相信很多人或多或少听说了微信公众平台的火热.但是开发还是有一点门槛,鉴于挺多朋友问我怎么开发,问多了,自己平时也进行以下总结.所以下面给大家分享一下我的经验: 微信公众号是什么? 官网的介绍:再小的个 ...

  3. 在 ServiceModel 客户端配置部分中,找不到引用协定“WebServiceTest.WebServiceSoap”的默认终结点元素。这可能是因为未找到应用程序的配置文件,或者是因为客户端元素

    原文  http://blog.csdn.net/bugdemo/article/details/9083497 主题 技术 在引用WebService后,程序运行到实例化WebService时报错, ...

  4. 【node.js】安装express后,'express' 不是内部或外部命令的问题

    因express默认安装是最新的版本,已经是4.x.x的版本.而最新express4.0+版本中将命令工具分出来了,所以必须要安装express-generator,执行: D:\TOOLS\Node ...

  5. Hive drop table batched

    if the hive version not support drop table tablename purge. your drop table command will move data t ...

  6. NPOI 读取excel到DataTable 读取隐藏列 读取公式列

    处理思路: 1.打开excel 用NPOI进行读取: 2.读取第一个Sheet: 读取过程中: a.先设置相应列 不隐藏 b.读取Cell时 先判断是否的包含公式 相应代码如下: public sta ...

  7. 【读书笔记《Android游戏编程之从零开始》】18.游戏开发基础(碰撞检测)

    1.矩形碰撞 所谓矩形碰撞,就是利用两个矩形之间的位置关系来进行判断,如果矩形的像素在另外一个矩形之中,或者之上都可以认为这两个矩形发生了碰撞. 如果单纯的去考虑哪些情况会判定两个矩形发生碰撞,倒不如 ...

  8. HTML设置超链接字体颜色和点击后的字体颜色

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  9. Java的文件读写操作 <转>

    目录: file内存----输入流----程序----输出流----file内存 java中多种方式读文件 判断文件是否存在不存在创建文件 判断文件夹是否存在不存在创建文件夹 java 写文件的三种方 ...

  10. android调试模拟器启动太慢,怎样才能更快的调试程序呢?

    答: 1. 模拟器不关,直接按调试按钮系统会自动重新安装软件的.