如何确保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.validate使用攻略 第一部
Jul 01 Javascript
jquery 学习之二 属性(类)
Nov 25 Javascript
JavaScript传递变量: 值传递?引用传递?
Feb 22 Javascript
Node.js和MongoDB实现简单日志分析系统
Apr 25 Javascript
jquery.qtip提示信息插件用法简单实例
Jun 17 Javascript
jQuery Easyui datagrid editor为combobox时指定数据源实例
Dec 19 Javascript
js点击任意区域弹出层消失实现代码
Dec 27 Javascript
深入理解Node中的buffer模块
Jun 03 Javascript
vue 不使用select实现下拉框功能(推荐)
May 17 Javascript
JavaScript实现简单的图片切换功能(实例代码)
Apr 10 Javascript
有关vue 开发钉钉 H5 微应用 dd.ready() 不执行问题及快速解决方案
May 09 Javascript
Vue封装全局过滤器Filters的步骤
Sep 16 Javascript
如何确保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
解决了Ajax、MySQL 和 Zend Framework 的乱码问题
2009/03/03 PHP
php park、unpark、ord 函数使用方法(二进制流接口应用实例)
2010/10/19 PHP
php读取文件内容的几种方法详解
2013/06/26 PHP
JoshChen_web格式编码UTF8-无BOM的小细节分析
2013/08/16 PHP
PHP goto语句简介和使用实例
2014/03/11 PHP
PHP 使用redis简单示例分享
2015/03/05 PHP
PHP中的访问修饰符简单比较
2019/02/02 PHP
使用Entrust扩展包在laravel 中实现RBAC的功能
2020/03/16 PHP
使javascript也能包含文件
2006/10/26 Javascript
用javascript实现自定义标签
2007/05/08 Javascript
javascript页面上使用动态时间具体实现
2014/03/18 Javascript
Firefox中使用outerHTML的2种解决方法
2014/06/07 Javascript
多个checkbox被选中时如何判断是否有自己想要的
2014/09/22 Javascript
sencha ext js 6 快速入门(必看)
2016/06/01 Javascript
JS实现的简单拖拽功能示例
2017/03/13 Javascript
vue+axios实现登录拦截的实例代码
2017/05/22 Javascript
JavaScript实现的联动菜单特效示例
2019/07/08 Javascript
layer父页获取弹出层输入框里面的值方法
2019/09/02 Javascript
React+Redux实现简单的待办事项列表ToDoList
2019/09/29 Javascript
JavaScript 面向对象程序设计详解【类的创建、实例对象、构造函数、原型等】
2020/05/12 Javascript
Vue 禁用浏览器的前进后退操作
2020/09/04 Javascript
Python中变量交换的例子
2014/08/25 Python
基于Python列表解析(列表推导式)
2018/06/23 Python
python利用requests库进行接口测试的方法详解
2018/07/06 Python
python Dijkstra算法实现最短路径问题的方法
2019/09/19 Python
Python实现京东抢秒杀功能
2021/01/25 Python
奥地利度假券的专家:we-are.travel
2019/04/10 全球购物
通信工程毕业生求职信
2013/11/16 职场文书
国贸专业的职业规划范文
2014/01/23 职场文书
办公自动化毕业生求职信
2014/03/09 职场文书
舞蹈专业大学生职业规划范文
2014/03/12 职场文书
学校教研活动总结
2014/07/02 职场文书
签订劳动合同通知书
2015/04/16 职场文书
2016最新离婚协议书范本及程序
2016/03/18 职场文书
详解Redis实现限流的三种方式
2021/04/27 Redis
《勇者辞职不干了》上卷BD发售宣传CM公开
2022/04/08 日漫