如何确保JavaScript的执行顺序 之实战篇


Posted in Javascript onMarch 03, 2011

1. 引言
我曾在文章《如何在多个页面使用同一个HTML片段 - 续》的最后提到JavaScript顺序执行的特性。虽然现代浏览器可以并行的下载JavaScript(部分浏览器),但考虑到JavaScript的依赖关系,他们的执行依然是按照引入顺序进行的。
为了更好的测试这个过程,我写了一个简单的HTTP处理程序页面 service.ashx,它可以接受两个参数:
1. file,需要返回文件的服务器端路径。
2. delay,延迟一定时间后再返回本次HTTP请求(毫秒)。
一个典型的页面比如:./service.ashx?file=js/jquery-ui.js&delay=2000,表示延迟2秒钟后再返回服务器端的js/jquery-ui.js文件。
service.ashx 的关键代码如下:

public void ProcessRequest(HttpContext context) 
{ 
int delay = 0; 
if (!String.IsNullOrEmpty(context.Request["delay"])) 
{ 
delay = Convert.ToInt32(context.Request["delay"]); 
} 
if (delay > 0) 
{ 
System.Threading.Thread.Sleep(1000); 
} 
string filePath = context.Request["file"].ToString(); 
string fileContent = String.Empty; 
using (StreamReader sr = new StreamReader(context.Server.MapPath(filePath))) 
{ 
fileContent = sr.ReadToEnd(); 
} 
if (filePath.EndsWith(".js")) 
{ 
context.Response.ContentType = "application/x-javascript"; 
} 
else 
{ 
context.Response.ContentType = "text/plain"; 
} 
context.Response.Write(fileContent); 
}

2. 通过script标签直接引入JavaScript(test1.htm)
首先我们分析下在<head>标签中顺序引入JavaScript的情况。test1.htm的页面源代码如下:
<html> 
<head> 
<title></title> 
<script src="./js/jquery-1.4.4.js" 
type="text/javascript"></script> 
<script src="./service.ashx?file=js/jquery-ui.js&delay=2000" 
type="text/javascript"></script> 
<script> 
alert(typeof (jQuery.ui)); 
</script> 
</head> 
<body> 
</body> 
</html>

我们分别在各种浏览器中测试这个例子:
test1.htm
通过script标签直接引入JavaScript
Firefox 3.6
IE 8
Chrome 10
Safari 4
Opera 11

 

可以看出各个主流浏览器的行为一致。虽然jQueryUI在服务器延迟了2秒钟再返回,但是后引入的内联JavaScript还是等待了2秒,等前面引入的JavaScript执行完毕才执行。这也是著名的JavaScript顺序执行的特性。
3. 通过JavaScript添加script标签(test3.htm)
我们首先定义一个addScript函数,用来引入外部或者内联JavaScript。test3.htm的页面源代码如下:

<html> 
<head> 
<title></title> 
<script src="./js/jquery-1.4.4.js" type="text/javascript"></script> 
<script> 
function addScript(url, inline) { 
var head = document.getElementsByTagName("head")[0]; 
var script = document.createElement('script'); 
script.type = 'text/javascript'; 
if (inline) { 
script.text = url; 
} else { 
script.src = url; 
} 
head.appendChild(script); 
} 
$(function () { 
addScript('./service.ashx?file=js/jquery-ui.js&delay=2000'); 
addScript('alert(typeof(jQuery.ui));', true); 
}); 
</script> 
</head> 
<body> 
<div id="container"> 
</div> 
</body> 
</html>

我们分别在各种浏览器中测试这个例子:
test3.htm
通过JavaScript添加<script>标签
Firefox 3.6
IE 8
Chrome 10
Safari 4
Opera 11

可见,通过JavaScriptDOM加载完毕后再引入外部或者内联JavaScript时,FirefoxOpera的行为一致,能够确保JavaScript的执行顺序和引入顺序一致。但是IE8, Chrome, Safari 却不能保证这个执行顺序。

虽然各种浏览器在确保执行顺序方面不尽相同,不过这时的最大好处是多个JavaScript文件能够并行下载,这在所有浏览器中行为一致。当然这不是这篇文章的主题,可以Google更多细节

如何解决各个浏览器的不一致性,下面提供了两个解决方案:
4. 方案一,如何在动态添加script标签时确保执行顺序
有时页面逻辑要求我们必须通过上面的方式动态执行JavaScript,那么如何确保所有浏览器下的执行顺序(目前只有Firefox和Opera确保执行顺序)。
其实解决方案很简单,我们为函数执行添加一个complete的回调函数就行了。下面的test4.htm给出了具体的解决方案:

<html> 
<head> 
<title></title> 
<script src="./js/jquery-1.4.4.js" type="text/javascript"></script> 
<script> 
function addScript(url, inline, callback) { 
var head = document.getElementsByTagName("head")[0]; 
var script = document.createElement('script'); 
script.type = 'text/javascript'; 
if (inline) { 
script.text = url; 
} else { 
script.src = url; 
script.onload = script.onreadystatechange = function () { 
if (!script.readyState || script.readyState === 'loaded' || script.readyState === 'complete') { 
if (callback) { 
callback(); 
} 
script.onload = script.onreadystatechange = null; 
}; 
}; 
} 
head.appendChild(script); 
if (inline && callback) { 
callback(); 
} 
} 
$(function () { 
addScript('./service.ashx?file=js/jquery-ui.js&delay=2000', false, function () { 
addScript('alert(typeof(jQuery.ui));', true); 
}); 
}); 
</script> 
</head> 
<body> 
<div id="container"> 
</div> 
</body> 
</html>

此时所有浏览器中的行为一致:
test4.htm
通过回调函数解决动态添加JavaScript的顺序问题
Firefox 3.6
IE 8
Chrome 10
Safari 4
Opera 11

5. 方案二,使用jQuery的html函数动态添加JavaScript
jQuery的html函数用来更新一个DOM片段,我们可以很方便的通过这个函数来动态加载JavaScript,请看示例test2.htm:
<html> 
<head> 
<title></title> 
<script src="js/jquery-1.4.4.js" type="text/javascript"></script> 
<script> 
$(function(){ 
$('#container').html('<script src="./service.ashx?file=js/jquery-ui.js&delay=2000" type="text\/javascript"><\/script>' + '<script>alert(typeof(jQuery.ui));<\/script>'); 
}); 
</script> 
</head> 
<body> 
<div id="container"> 
</div> 
</body> 
</html>

此时,各个浏览器中的行为一致:
test2.htm
通过jQuery的html函数解决动态添加JavaScript的顺序问题
Firefox 3.6
IE 8
Chrome 10
Safari 4
Opera 11

6. 后记
为什么jQuery的html函数能够确保动态加载JavaScript的执行顺序呢?
我们知道通过简单的 .innerHTML 更新DOM节点,是不会让其中的JavaScript的执行,我们可以简单的把这个例子的源代码改成:
$('#container')[0].innerHTML = '<script src="./service.ashx?file=js/jquery-ui.js&delay=2000" type="text\/javascript"><\/script>' + '<script>alert(typeof(jQuery.ui));<\/script>';
这种情况下jQueryUI根本不会加载。
那么jQuery是如果做到的呢?下篇文章我们会追根溯源,详细分析jQuery源代码,请继续浏览: 如何确保JavaScript的执行顺序 ? 之jQuery.html深度分析

Javascript 相关文章推荐
jQuery 处理网页内容的实现代码
Feb 15 Javascript
jq实现酷炫的鼠标经过图片翻滚效果
Mar 12 Javascript
动态显示可输入的字数提示还可以输入的字数
Apr 01 Javascript
JavaScript lastIndexOf方法入门实例(计算指定字符在字符串中最后一次出现的位置)
Oct 17 Javascript
jQuery中ajax的load()方法用法实例
Dec 26 Javascript
jquery实现标签上移、下移、置顶
Apr 26 Javascript
jQuery插件Validation快速完成表单验证的方式
Jul 28 Javascript
JavaScript中子对象访问父对象的方式详解
Sep 01 Javascript
Bootstrap模态框插件使用详解
May 11 Javascript
解决微信小程序调用moveToLocation失效问题【超简单】
Apr 12 Javascript
Javascript数组及类数组相关原理详解
Oct 29 Javascript
vue3弹出层V3Popup实例详解
Jan 04 Vue.js
如何确保JavaScript的执行顺序 之jQuery.html并非万能钥匙
Mar 03 #Javascript
如何确保JavaScript的执行顺序 之jQuery.html深度分析
Mar 03 #Javascript
jQuery 操作option的实现代码
Mar 03 #Javascript
基于Jquery的$.cookie()实现跨越页面tabs导航实现代码
Mar 03 #Javascript
jquery中实现简单的tabs插件功能的代码
Mar 02 #Javascript
基于jQuery的简单的列表导航菜单
Mar 02 #Javascript
jquery异步调用页面后台方法&amp;#8207;(asp.net)
Mar 01 #Javascript
You might like
PHP生成不同颜色、不同大小的tag标签函数
2013/09/23 PHP
为PHP安装imagick时出现Cannot locate header file MagickWand.h错误的解决方法
2014/11/03 PHP
PHP如何通过AJAX方式实现登录功能
2015/11/23 PHP
PHP-CGI远程代码执行漏洞分析与防范
2017/05/07 PHP
Textarea与懒惰渲染实现代码
2012/01/04 Javascript
jQuery学习笔记(1)--用jQuery实现异步通信(用json传值)具体思路
2013/04/08 Javascript
node+express+ejs制作简单页面上手指南
2014/11/26 Javascript
jQuery中click事件的定义和用法
2014/12/20 Javascript
基于jQuery Bar Indicator 插件实现进度条展示效果
2015/09/30 Javascript
基于jQuery实现放大镜特效
2020/10/19 Javascript
微信小程序-消息提示框实例
2016/11/24 Javascript
Vue实现双向绑定的方法
2016/12/22 Javascript
javascript常用的设计模式
2017/02/09 Javascript
深入理解JavaScript继承的多种方式和优缺点
2017/05/12 Javascript
EasyUI的DataGrid绑定Json数据源的示例代码
2017/12/16 Javascript
vue权限管理系统的实现代码
2019/01/17 Javascript
highcharts.js数据绑定方式代码实例
2019/11/13 Javascript
解决antd Form 表单校验方法无响应的问题
2020/10/27 Javascript
详解Python判定IP地址合法性的三种方法
2018/03/06 Python
使用DataFrame删除行和列的实例讲解
2018/04/08 Python
Tesserocr库的正确安装方式
2018/10/19 Python
Python中注释(多行注释和单行注释)的用法实例
2019/08/28 Python
python dataframe NaN处理方式
2019/12/26 Python
python实现批量转换图片为黑白
2020/06/16 Python
Python内置函数property()如何使用
2020/09/01 Python
python Cartopy的基础使用详解
2020/11/01 Python
iframe与window.onload如何使用详解
2020/05/07 HTML / CSS
澳大利亚家庭花园和DIY工具网店:VidaXL
2019/05/03 全球购物
巴西本土电商平台:Americanas
2020/06/21 全球购物
C/C++有关内存的思考题
2015/12/04 面试题
社团招新策划书
2014/02/04 职场文书
八一建军节演讲稿
2014/09/10 职场文书
应届毕业生求职简历自我评价
2015/03/02 职场文书
党性教育心得体会(共6篇)
2016/01/21 职场文书
详解Apache SkyWalking 告警配置指南
2021/04/22 Servers
Python列表的索引与切片
2022/04/07 Python