首页 > 代码库 > 异步加载脚本保持执行顺序

异步加载脚本保持执行顺序

首先是外部脚本和行内脚本,对于异步加载的脚本,会导致竞争状态,使得出现未定义的错。

采用Script Dom技术测试:

代码:

<script type="text/javascript"> 

  var scriptElem = document.createElement(‘script‘);
  scriptElem.src = "http://www.mamicode.com/js/jquery-2.1.1.js";

  document.getElementsByTagName(‘head‘)[0].appendChild(scriptElem);

</script>

<script type="text/javascript">

     function test(){

        $("#test").addClass(‘class_name‘); }

     test();

</script>

运行结果:

技术分享

技术分享

以下几种方式解决该问题:

1.硬编码回调

将test方法的执行定义在外部脚本(即调用的脚本),该方法不灵活,如果调用的是第三方脚本的话,更加麻烦。此处不显示例子。

 

2.Window onl oad
通过监听window的onload事件来触发行内代码的执行。只要确保外部脚本在window。Onload之前下载执行就可以保持执行顺序。

运行结果:

技术分享

 

技术分享

代码:

function test(){

  $("#test").addClass(‘class_name‘);

  }

  if(window.addEventListener){

     window.addEventListener("load", test, false);

  }else if(window.attachEvent){

     window.attachEvent("onload",test);

  }

 

 

缺点:1.必须确保异步脚本是通过阻塞onload事件的方式加载的。

 

         2.如果页面有更多的资源,那么外部脚本可能在onload时间出发之前早就完成加载,一般来说,行内脚本最好在外部脚本下载和执行完成之后立即调用。

 

3.定时器:

采用轮询方法来抱着在行内脚本执行之前所依赖的外部脚本已经加载。

运行结果:

技术分享

技术分享

 

代码:

function test(){

  $("#test").addClass(‘class_name‘);

  console.log(index);

  }

var index = 0;

  function initTimer(){

    if("undefined" === typeof($)){

      index++;

      setTimeout(initTimer,300)

    }else{

      test();

    }

  }

  initTimer();

 

缺点:如果在setTimeout方法中设置的时间太小,会造成额外的开销。设置太大会导致和windon.onload的方法一样,脚本加载完成无法立即执行行内脚本。另外,如果脚本出错,轮询会无限进行下去。

 

4.Script onl oad

前面提到的整合技术会增加页面的脆弱性、延迟和开销,通过监听脚本的onload事件可以解决这些问题。

运行结果:

技术分享

 

技术分享

代码:

function test(){

  $("#test").addClass(‘class_name‘);

  }

var scriptElem = document.createElement(‘script‘);

  scriptElem.src = "http://www.mamicode.com/js/jquery-2.1.1.js";

  scriptElem.onloadDone = false;

  scriptElem.onload = function(){

    scriptElem.onloadDone = true;

    test();

  }

  scriptElem.onreadyStatechange  = function(){

    if(("loaded" === scriptElem.readyState || "complete" === scriptElem.readyState) && !scriptElem.onloadDone){

       scriptElem.onloadDone = true;

    test();

    }

  }

  document.getElementsByTagName(‘head‘)[0].appendChild(scriptElem);

 

优点:维护简单,事件处理也简单,整合异步加载外部脚本和行内脚本的首选。

 

 

5.降级使用script标签:

即用一个标签即包含外部脚本,又使用行内脚本,如下:

<script src="http://www.mamicode.com/js/jquery-2.1.1.js"  >

  function test(){

  $("#test").addClass(‘class_name‘);

  }

</script>

由于浏览器并不支持这种模式,所以需要在脚本的内部增加代码来执行行内脚本,找到该脚本,并用eval执行其内容。如下:

var scripts = document.getElementsByTagName(‘script‘);

         var cntr = scripts.length;

         while(cntr--){

      var curScript = scripts[cntr-1];

      if(-1 != curScript.src.indexOf("js/jquery-2.1.1.js")){

             eval(curScript.innerHTML);

             break;

      }

      cntr--;

         }

此处不给出例图,具体代码如下:

function test(){

    $("#test").addClass(‘class_name‘);

  }

  var scriptElem = document.createElement(‘script‘);

  scriptElem.src = "http://www.mamicode.com/js/jquery-2.1.1.js";

  if(-1 != navigator.userAgent.indexOf("Opera")){

       scriptElem.innerHTML = "test()";

  }else{

       scriptElem.text = "test()";

  }

document.getElementsByTagName(‘head‘)[0].appendChild(scriptElem);

 

优点:技术优雅简洁,开销最小。

缺点:需要修改外部脚本,对第三方库不适用。

异步加载脚本保持执行顺序