代码改变世界

Xhr异步按顺序加载script

2010-05-10 23:05  BlueDream  阅读(2093)  评论(1编辑  收藏  举报

在同域的情况下.无阻塞异步加载js的比较完美的方法就是通过XHR eval动态加载解析外部js文件.但XHR的问题就是.无法保证加载顺序.只是哪个.js文件先加载完毕就先执行哪个.所以我们需要用个队列机制将其管理.提供顺序引入的功能.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title> new document </title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>
<body>
<script>
if ( 'undefined' == typeof(System) || !System ) {
    var System = {};
} 
/**
 *    Xhr -- Eval -- Order
 *    xhr异步按顺序动态执行javascript
 */
 
System.Script = {
    // 存放script请求队列
    queuedScripts: new Array(),
    /**
     *    引入执行.js文件函数
     *    @param (String) url 要引入执行的.js文件路径
     *    @param (Function) onload 当引入.js文件执行完毕后执行的函数
     *  @param (Boolean) order 是否要按顺序引入
     */
    load: function(url, onload, order) {
        // 队列长度 数组形式(自动递增)
        var iQueue = System.Script.queuedScripts.length;
        // 如果引入的多个.js文件有依赖关系,那么我们需要order为true
        if ( order ) {
            // 维护js请求相对应的数组队列.并分别赋予一张默认的hash表
            var qScript = { response: null, onload: onload, done: false };
            System.Script.queuedScripts[iQueue] = qScript;
        } 
        var xhrObj = this.getXHRObj();
        xhrObj.onreadystatechange = function() {
            // 获取xhr.并当xhr正确请求时
            if ( xhrObj.readyState == 4 ) {
                // 如果是有顺序的话.那么要讲所有的请求都按顺序放入到队列中.并将每次请求返回的js文本赋给response键值
                if ( order ) {
                    System.Script.queuedScripts[iQueue].response = xhrObj.responseText;
                    // 紧接着开始按照队列顺序注入js
                    System.Script.injectScripts();
                } else {
                    // 如果不需要按照顺序执行的话.那么就谁先加载完毕谁就直接通过Script.text去执行.执行完毕后.如果指定了onload则运行onload
                    var se = document.createElement('script');
                    document.getElementsByTagName('head')[0].appendChild(se);
                    se.text = xhrObj.responseText;
                    if ( onload ) {
                        onload();
                    } 
                }
            } 
        };
        xhrObj.open('GET', url, true);
        xhrObj.send('');
    },
    /**
     *    获取Xhr
     */
    getXHRObj: function() {
        var methods = [
            function() { return new XMLHttpRequest(); },
            function() { return new ActiveXObject('Xml2.XMLHTTP'); },
            function() { return new ActiveXObject('Microsoft.XMLHTTP'); }
        ];
        for ( var i = 0, len = methods.length; i < len; i++ ) {
            try {
                methods[i]();
                this.getXHRObj = methods[i];
                break;
            } catch(e) {}
        } 
        return methods[i]();
    },
    /**
     *    用于按顺序引用JS.从队列中顺序注入javascript
     */
    injectScripts: function() {
        // 开始循环注入js
        var len = System.Script.queuedScripts.length;
        for ( var i = 0; i < len; i++ ) {
            var qScript = System.Script.queuedScripts[i];
            if ( !qScript.done ) {
                // 如果有一个script请求没有返回.那么就中断.
                if ( !qScript.response ) {
                    break;
                } else {
                    var se = document.createElement('script');
                    document.getElementsByTagName('head')[0].appendChild(se);
                    se.text = qScript.response;
                    if ( qScript.onload ) {
                        qScript.onload();
                    } 
                    qScript.done = true;
                }
            } 
        }
    }
};

function init() {
    document.title += 'init function';
}

System.Script.load('main.js', null, true);
System.Script.load('sub.js', init, true);
</script>
</body>
</html>

上面的代码就是一个具有队列性质的管理机制.

用法就是:

System.Script.load('main.js', null, true);
System.Script.load('sub.js', init, true);

表示引入两个.js文件.顺序为先main.js然后再sub.js并且当sub.js执行完毕后会调用init方法.