天天看點

js的并行加載以及順序執行

重新溫習了下這段内容,發現各個浏覽器的相容性真的是搞大了頭,處理起來很是麻煩。

現在現總結下并行加載多個js的方法:

  1,對于動态createElement('script')的方式,對所有浏覽器都是異步并行加載的。這裡所說的并行不僅僅指的是

    js并行加載,也包括js和其他資源比如圖檔,iframe的加載。但是此種方式在Firefox的2.0 3.0 3.1版本和opera 9.63

    下是可以順序執行的。但是由于Kyle的提議,現代浏覽器都可以通過對動态建立的script元素設定屬性async=false來使

    js順序執行。

  2,可以通過document.write('<script>')的方式來并行加載(IE,現代浏覽器)和順序執行。

  3,通過xhr加載js。但是有了同源的限制,是以對于外部js檔案或者cdn上的js就無能為力。

已經有些大牛比如之前提到的Kyle已經提供了相容個浏覽器的标準庫,項目名稱是 LABjs。

自己寫了一個簡單的插件,目前并沒有在IE6,7上測試。

  

if(!asyncHelper) var asyncHelper = {};
        asyncHelper.cache = []; //存儲擷取到的js對象

        asyncHelper.createAjax = (function(){
            if('XMLHttpRequest' in window){
                return function(){
                    return new XMLHttpRequest();
                }
            }else{
                var i= 0,len, fns = [function(){return new ActiveXObject('Microsoft.XMLHTTP')},function(){return new ActiveXObject('Msxml2.XMLHTTP')},
                    function(){return new ActiveXObject('Msxml2.XMLHTTP.3.0')},function(){return new ActiveXObject('Msxml2.XMLHTTP.6.0')}];

                for(len = fns.length;i<len;i++){
                    try{
                        fns[i]();
                        return fns[i];
                        break;
                    }catch (e){
                    }
                }
            }
        })();
        //功能函數,異步xhr加載js,并行無序加載js和其他資源,需要進行順序控制;而且受同源限制,
        //無法使用cdn或外部引用js
        asyncHelper._loadJsWithXHR = function(url,fn,inOrder){
            inOrder = inOrder || true; //預設順序加載
            var jsObj = {file: null,isLoaded:false,callback: fn},xhr,
                    i,len;
            asyncHelper.cache.push(jsObj);
            xhr = this.createAjax();
            xhr.onreadystatechange = function(){
                if(xhr.readyState == 4){
                    try{
                        if(xhr.status >=200 && xhr.status < 300 || xhr.status == 304){
                            jsObj.file = xhr.responseText; // 傳回的js存入對象中
                            if(inOrder){
                                for(i=0,len=asyncHelper.cache.length;i<len;i++){
                                    if(!asyncHelper.cache[i].file){
                                        //避免重複解析已加載過得js檔案
                                        //從緩沖彙總删除已經加載的檔案
                                        if(i>0){
                                            asyncHelper.cache.splice(0,i);
                                        }
                                        break;
                                    }else{
                                        //Function相當于全局eval,不會改變作用域鍊
                                        new Function(asyncHelper.cache[i].file)();
                                        fn && fn(); //執行回調函數
                                        if(i == len-1){
                                            asyncHelper.cache = []; //清空緩存
                                        }
                                    }
                                }
                            }else{
                                if(jsObj.file){
                                    eval(jsObj.file);
                                    fn();
                                }
                            }
                        }
                    }catch (loadError){
                        setTimeout(function(){
                            throw(new Error('loading with XHR response error--' + loadError))
                        },0);
                    }

                }
            };
            xhr.open('get',url);
            xhr.setRequestHeader('X-Request-With','XMLHttpRequest');
            xhr.send(null);
        };

        //通過建立script元素來異步加載js,支援跨域。在firefox,opera下也是順序加載。
        asyncHelper._loadJsWithDOMElement = function(url,fn){
            var dom = document.createElement('script');
            dom.type = 'application/javascript';
            dom.async = false;
            dom.src = url;
            dom.isloaded = false;
            //執行回調函數,IE下使用onreadystatechange,w3c使用onload
            if('onload' in dom){
                dom.onload = fn;
            }else{
                dom.onreadystatechange = function(){
                    if((dom.readyState == 'loaded' || dom.readyState == 'complete') &&
                            !dom.isloaded){
                        fn();
                        dom.isloaded = true;
                    }
                }
            }
            document.getElementsByTagName('head')[0].appendChild(dom);
        }

        //通過document.write插入script來進行并行加載腳本。gte IE8以及opera支援。
        //全部浏覽器支援此種方式的順序加載js
        asyncHelper._loadJsWithScriptTag = function(url,fn){
            document.writeln('<script type="application/javascript" src="' +
                url +'"><\/script>');
            //給window綁定onload事件
            if(window.addEventListener){
                window.addEventListener('load',fn,false);
            }else{
                window.attachEvent('onload',function(){
                    fn.call(this,window.event);
                })
            }
        }

        //暴露外部接口,加載單個js檔案
        asyncHelper.loadScript = function(url,fn){
            this._loadJsWithDOMElement(url,fn);
        }

        //加載多個js檔案
        asyncHelper.loadScripts = function(urls,fn){
            function isSameDomain(url){
                var domain = document.location.protocol + "//" +
                        document.location.hostname + "/";
                if(url.indexOf('http') !== -1 || url.indexOf('https') !== -1){
                    if(url.indexOf(domain) !== -1){
                        return true;
                    }
                    return false;
                }
                return true;
            }

            //如果url同源,則使用xhr加載
            var i,len,flag,loadMethod;
            for(i=0,len=urls.length;i<len;i++){
                if(flag = isSameDomain(urls[i])) continue;
                else break;
            }
            //預設xhr加載
            loadMethod = asyncHelper._loadJsWithXHR;
            if(!flag){
                //firefox opera使用DomElement方式加載,確定順序性和異步加載
                // 經測試,目前最新版本的Firefox亦不支援此特性。
                //Firefox 4為了更向HTML5标準看齊,一度在開發者版本中去掉了對動态建立<script>來加載js檔案的執行順序支援:
                //<script> elements created using document.createElement() and inserted into a document now behave
                // according to the HTML5 specification by default. Scripts with the src attribute
                // execute as soon as available (without maintaining ordering) and scripts without
                // the src attribute execute synchronously.

                //Kyle向WebKit開發團隊抗議,提了一個bug,最終得到了如他所願的支援:
                //To make script-inserted scripts that have the src attribute execute in the insertion order,
                // set .async=false on them.
                if(navigator.userAgent.toLowerCase().indexOf('firefox') != -1 || navigator.userAgent.toLowerCase().indexOf('opera') != -1)
                    loadMethod = asyncHelper._loadJsWithDOMElement;
                else
                    loadMethod = asyncHelper._loadJsWithScriptTag;

            }for(i=0;i<len;i++){
                if(i == len - 1){
                    loadMethod.call(asyncHelper,urls[i],fn);
                }else{
                    loadMethod.call(asyncHelper,urls[i]);
                }
            }
        }

// 示例代碼  
asyncHelper.loadScripts(['http://libs.baidu.com/jquery/1.9.0/jquery.js','./a.js','./b.js'],function(){console.log('success')})      
上一篇: 節點插入

繼續閱讀