解析JavaScript异步加载

本篇文章给你们分享了做者在学习JavaScript异步加载中遇到的问题,总结后给出了详细的处理方案,写的十分的全面细致,具备必定的参考价值,对此有须要的朋友能够参考学习下。若有不足之处,欢迎批评指正。javascript

同步加载的问题html

默认的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代码放到前不就能先渲染页面了!好方法,咱们尝试着将js放后面:html5

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"也很快出如今前台,世界彷佛平静了,但是:java

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>//欢迎加入前端全栈开发交流圈一块儿学习交流:864305860
复制代码

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

实现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);
  }();//欢迎加入前端全栈开发交流圈一块儿学习交流:864305860
 </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);
   // }//欢迎加入前端全栈开发交流圈一块儿学习交流:864305860
   // window.addEventListener('load', async_load, false);
  }();
 
  window.onload = function() {
   var txt = document.createTextNode(' hello world');
   document.body.appendChild(txt);
  };//欢迎加入前端全栈开发交流圈一块儿学习交流:864305860
 </script>
 <script type="text/javascript" src='http://libs.baidu.com/jquery/2.0.0/jquery.min.js'></script>
</body>
复制代码

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

补充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>//欢迎加入前端全栈开发交流圈一块儿学习交流:864305860
复制代码

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>//欢迎加入前端全栈开发交流圈一块儿学习交流:864305860
复制代码

彷佛实现效果差很少,可是真的同样吗?咱们来看看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的区别):

  • defer 和 async 在网络读取(下载)这块儿是同样的,都是异步的(相较于 HTML 解析)
  • 它俩的差异在于脚本下载完以后什么时候执行,显然 defer 是最接近咱们对于应用脚本加载和执行的要求的
  • 关于 defer,此图未尽之处在于它是按照加载顺序执行脚本的,这一点要善加利用
  • async 则是一个乱序执行的主,反正对它来讲脚本的加载和执行是牢牢挨着的,因此无论你声明的顺序如何,只要它加载完了就会马上执行
  • 仔细想一想,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>//欢迎加入前端全栈开发交流圈一块儿学习交流:864305860
<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方式等。

结语

感谢您的观看,若有不足之处,欢迎批评指正。

为了帮助你们让学习变得轻松、高效,给你们免费分享一大批资料,帮助你们在成为全栈工程师,乃至架构师的路上披荆斩棘。在这里给你们推荐一个前端全栈学习交流圈:864305860 欢迎你们进群交流讨论,学习交流,共同进步。

当真正开始学习的时候不免不知道从哪入手,致使效率低下影响继续学习的信心。

但最重要的是不知道哪些技术须要重点掌握,学习时频繁踩坑,最终浪费大量时间,因此有有效资源仍是颇有必要的。

最后祝福全部遇到瓶疾且不知道怎么办的前端程序员们,祝福你们在日后的工做与面试中一切顺利。

相关文章
相关标签/搜索