博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
异步加载脚本保持执行顺序
阅读量:6882 次
发布时间:2019-06-27

本文共 6310 字,大约阅读时间需要 21 分钟。

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

采用Script Dom技术测试:

代码:

 

 

 

 

运行结果:

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

1.硬编码回调

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

 

2.Window onload

通过监听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 onload

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

运行结果:

 

代码:

 

function test(){  $("#test").addClass('class_name');  }var scriptElem = document.createElement('script');  scriptElem.src = "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标签:

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

 

 

 

 

由于浏览器并不支持这种模式,所以需要在脚本的内部增加代码来执行行内脚本,找到该脚本,并用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 = "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);

 

 

 

 

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

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

 

 

 

多个脚本按序执行:

正常引入脚本:

运行结果:

采用XHR eval:

运行结果:

由于脚本没有按顺序执行,出现未定义的错误。

 

 

解决方法1Managed XHR

通过EFWS.Script模块封装了一种技术,将XHR响应加入队列来保证它们按顺序执行。

代码:

/*  数组queuedScripts存储执行队列中的脚本,每个脚本是拥有三个属性的对象:  response: XHR响应  onload: 脚本加载后触发的函数  bOrder: 如果该脚本需要依赖其他脚本按顺序执行,则设为true*/  EFWS.Script = {    queuedScripts: [],    //传入三个参数,第二个参数可选    loadScriptXhrInjection: function(url, onload, bOrder) {        var iQ = EFWS.Script.queuedScripts.length;    //如果需要按顺序执行,并将脚本对象放入数组        if (bOrder) {            var qScript = {response: null, onload: onload, done: false};            EFWS.Script.queuedScripts[iQ] = qScript;        }    //调用AJAX        var xhrObj = EFWS.Script.getXHRObject();        xhrObj.onreadystatechange = function() {            if (xhrObj.readyState == 4) {    //如果第三个参数的值为true,即调用injectScripts()函数                if (bOrder) {                    EFWS.Script.queuedScripts[iQ].response = xhrObj.responseText;                    EFWS.Script.injectScripts();    //如果不需要按顺序执行,即立即加载脚本                } else {                    eval(xhrObj.responseText);                    if (onload) {                        onload();                    }                }            }        };        xhrObj.open('GET', url, true);        xhrObj.send('');    },    //遍历数组,当发现某一脚本加载但未执行时,立即执行    injectScripts: function() {        var len = EFWS.Script.queuedScripts.length;        for (var i = 0; i < len; i++) {            var qScript = EFWS.Script.queuedScripts[i];    //已加载的脚本            if (!qScript.done) {    //如果响应未返回 立即停止                if (!qScript.response) {                    break;    //执行脚本                } else {                    eval(qScript.response);                    if (qScript.onload) {                        qScript.onload();                    }                    qScript.done = true;                }            }        }    },    //AJAX对象    getXHRObject: function() {        var xhrObj = false;        try {            xhrObj = new XMLHttpRequest();        }        catch(e) {            var aTypes = ["Msxm12.XMLHTTP6.0",                          "Msxm12.XMLHTTP3.0",                          "Msxm12.XMLHTTP",                          "Microsoft.XMLHTTP"];            var len = aTypes.length;            for (var i = 0; i < len; i++) {                try {                    xhrObj = new ActiveXObject(aTypes[i]);                }                catch(e) {                    continue;                }                break;            }        }        finally {            return xhrObj;        }    }};//调用脚本EFWS.Script.loadScriptXhrInjection("js/jquery-2.1.1.js", null, true);EFWS.Script.loadScriptXhrInjection("js/first.js", null, true);EFWS.Script.loadScriptXhrInjection("js/second.js", null, true);

 运行结果:

缺点:必须同域。

 

当脚本不同域时,可以采用Script Dom Element document.write Script Tag的方法。

由于document.write Script Tag在并行下载脚本时会阻塞其他资源,而Script Dom Element则只在FireFox(实际测试FireFox并不行,可能是版本原因)Opeare按序执行,所以应在不同浏览器采用不同方法。

代码:

var ScriptLoader ={};   ScriptLoader.script = {       loadScriptDomElement:function(url, onload){            var script = document.createElement ("script")            script.type = "text/javascript";            if (script.readyState){ //IE                script.onreadystatechange = function(){                    if (script.readyState == "loaded" || script.readyState == "complete"){                        script.onreadystatechange = null;                        if(onload)                        onload();                    }                };            } else { //Others                script.onload = function(){                if(onload)                    onload();                };                        }            script.src = url;            document.getElementsByTagName("head")[0].appendChild(script);        },            loadScriptDomWrite: function(url,onload){            document.write('

运行结果(write):

 

 

转载于:https://www.cnblogs.com/Darlietoothpaste/p/6518631.html

你可能感兴趣的文章
Axure RP-->如何使生成的原型页面左侧不带框架(转)
查看>>
(1)安装----anaconda3下配置pyspark【单机】
查看>>
怎么提高点数据加载速度呢?
查看>>
CentOS 7 systemd的坑
查看>>
WPF学习笔记:获取ListBox的选中项
查看>>
FeatureLayer.MODE_SNAPSHOT限制数量问题
查看>>
Go数据结构之Queue
查看>>
例题7-6 UVa140 Bandwidth(枚举+剪枝)
查看>>
java中static关键字的作用
查看>>
使用UIPageControl UIScrollView制作APP引导界面
查看>>
PyQt4 ShowHMDB show sqlite3 with QTableWidget summary
查看>>
你所能用到的BMP格式介绍(一)
查看>>
题目1489:计算两个矩阵的乘积
查看>>
raid
查看>>
LoadRunner学习第三天
查看>>
[转]缺少 ; (在标识符 PhysicalMediumType 的前面)
查看>>
redis
查看>>
C++11中的tuple应用:让函数返回多个值
查看>>
JSON.parse()和JSON.stringify()
查看>>
MySQL自学2018/03/30-DML语句
查看>>