Javascript,按插入dom树的顺序执行脚本代码

Javascript,按插入dom树的顺序执行脚本代码,javascript,Javascript,不是重复的,因为我还没有在其他线程上找到满意的答案: 正在查找本机Javascript答案、无jQuery、无requireJS等等,请:) 整个问题的摘要: function loadScript(path, callback, errorCallback, options) { var element = document.createElement('script'); element.setAttribute("typ

不是重复的,因为我还没有在其他线程上找到满意的答案:


正在查找本机Javascript答案、无jQuery、无requireJS等等,请:)


整个问题的摘要:

    function loadScript(path, callback, errorCallback, options) {
            var element = document.createElement('script');
            element.setAttribute("type", 'text/javascript');
            element.setAttribute("src", path);

            return loadElement(element, callback, errorCallback, options);
    }

    function loadElement(element, callback, errorCallback, options) {
            element.setAttribute("defer", "");
            // element.setAttribute("async", "false");

            element.loaded = false;

            if (element.readyState){  // IE
                    element.onreadystatechange = function(){
                            if (element.readyState == "loaded" || element.readyState == "complete"){
                                    element.onreadystatechange = null;

                                    loadElementOnLoad(element, callback);
                            }
                    };
            } else {                 // Others
                    element.onload = function() {
                            loadElementOnLoad(element, callback);
                    };
            }

            element.onerror = function() {
                    errorCallback && errorCallback(element);
            };

            (document.head || document.getElementsByTagName('head')[0] || document.body).appendChild(element);

            return element;
    }

    function loadElementOnLoad(element, callback) {
            if (element.loaded != true) {
                    element.loaded = true;
                    if ( callback ) callback(element);
            }
    }




loadScript("http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js",function() {
  alert(1);  
})

loadScript("http://ajax.googleapis.com/ajax/libs/chrome-frame/1.0.3/CFInstall.min.js",function() {
  alert(2);  
})
我想异步加载脚本,但已按顺序执行 我试图强制执行插入的脚本元素中的代码的执行顺序与添加到dom树中的顺序完全相同

也就是说,如果我插入两个脚本标记,第一个和第二个,第一个中的任何代码都必须在第二个之前触发,无论谁先完成加载

我试过在插入头部时使用async属性和defer属性,但似乎没有遵守

我已经尝试了element.setAttribute(“defer”和element.setAttribute(“async”,false)和其他组合

我目前遇到的问题是在包含外部脚本时必须执行,但这也是我在存在延迟的情况下执行的唯一测试

第二个脚本是本地脚本,它总是在第一个脚本之前触发,即使它是在dom树(head)中插入的

A) 注意我仍在尝试将这两个脚本元素插入DOM。当然,通过插入第一个,让它完成并插入第二个,可以实现上述目标,但我希望有另一种方法,因为这可能会很慢

我的理解是,RequireJS似乎正在这样做,所以这应该是可能的。然而,requireJS可能会通过按A)中所述的方式来实现

如果您想直接在firebug中尝试,只需复制并粘贴代码:

    function loadScript(path, callback, errorCallback, options) {
            var element = document.createElement('script');
            element.setAttribute("type", 'text/javascript');
            element.setAttribute("src", path);

            return loadElement(element, callback, errorCallback, options);
    }

    function loadElement(element, callback, errorCallback, options) {
            element.setAttribute("defer", "");
            // element.setAttribute("async", "false");

            element.loaded = false;

            if (element.readyState){  // IE
                    element.onreadystatechange = function(){
                            if (element.readyState == "loaded" || element.readyState == "complete"){
                                    element.onreadystatechange = null;

                                    loadElementOnLoad(element, callback);
                            }
                    };
            } else {                 // Others
                    element.onload = function() {
                            loadElementOnLoad(element, callback);
                    };
            }

            element.onerror = function() {
                    errorCallback && errorCallback(element);
            };

            (document.head || document.getElementsByTagName('head')[0] || document.body).appendChild(element);

            return element;
    }

    function loadElementOnLoad(element, callback) {
            if (element.loaded != true) {
                    element.loaded = true;
                    if ( callback ) callback(element);
            }
    }




loadScript("http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js",function() {
  alert(1);  
})

loadScript("http://ajax.googleapis.com/ajax/libs/chrome-frame/1.0.3/CFInstall.min.js",function() {
  alert(2);  
})

如果您像firebug一样尝试上面的代码,它通常会触发2,然后触发1。我想先确保1,然后确保2,但在头部包括这两个

您不能使用您的回调嵌套加载吗

即:

如果我插入两个脚本标记,第一个和第二个,第一个中的任何代码都必须在第二个之前触发,无论谁先完成加载。我试过使用async属性和defer属性

不,
async
defer
在这里对您没有帮助。无论何时将脚本元素动态插入DOM,它们都会异步加载和执行。你不能做任何反对的事

我的理解是,RequireJS似乎正在这样做

不可以。即使使用RequireJS,脚本也是异步执行的,而且没有顺序。只有那些脚本中的模块初始化器函数才是
define()
d,而不是执行。然后,Requirejs会查看它们的依赖关系何时得到满足,并在其他模块加载后执行它们


当然,你可以重新发明轮子,但你必须采用一种类似于requirejs的结构。

经过一段时间的摆弄之后,我想到了这个。对脚本的请求会立即发出,但它们只能按指定的顺序执行

算法:

算法是维护一个需要执行的脚本树(我没有时间实现它:现在只是列表的退化情况)。所有这些请求几乎同时发送。每次加载脚本时,都会发生两件事:1)将脚本添加到已加载脚本的平面列表中;2)从根节点向下,执行每个分支中已加载但尚未执行的许多脚本

关于这一点,最酷的是,并非所有脚本都需要加载才能开始执行

实现:

出于演示目的,我在
scriptsToExecute
数组上反向迭代,以便
CFInstall
请求在
angularJS
请求之前发出。这并不一定意味着
CFInstall
将在
angularJS
之前加载,但这样做的可能性更大。不管怎样,
angularJS
总是在
CFInstall
之前进行评估

注意,就创建iframe元素和分配负载处理程序而言,我使用jQuery使我的工作更轻松,但您可以在不使用jQuery的情况下编写:

// The array of scripts to load and execute

var scriptsToExecute = [
    "http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js?t=" + Date.now(), 
    "http://ajax.googleapis.com/ajax/libs/chrome-frame/1.0.3/CFInstall.min.js?t=" + Date.now()
];


// Loaded scripts are stored here

var loadedScripts = {};


// For demonstration purposes, the requests are sent in reverse order.
// They will still be executed in the order specified in the array.

(function start() {
    for (var i = scriptsToExecute.length - 1; i >= 0; i--) {
        (function () {
            var addr = scriptsToExecute[i];
            requestData(addr, function () {
                console.log("loaded " + addr);
            });
        })();
    }
})();


// This function executes as many scripts as it currently can, by
// inserting script tags with the corresponding src attribute. The
// scripts aren't reloaded, since they are in the cache. You could
// alternatively eval `script.code`

function executeScript(script) {
    loadedScripts[script.URL] = script.code

    while (loadedScripts.hasOwnProperty(scriptsToExecute[0])) {
        var scriptToRun = scriptsToExecute.shift()
        var element = document.createElement('script');
        element.setAttribute("type", 'text/javascript');
        element.setAttribute("src", scriptToRun);

        $('head').append(element);

        console.log("executed " + scriptToRun);
    }
}


// This function fires off a request for a script

function requestData(path, loadCallback) {
    var iframe = $("<iframe/>").load(function () {
        loadCallback();
        executeScript({
            URL: $(this).attr("src"),
            code: $(this).html()
        });
    }).attr({"src" : path, "display" : "none"}).appendTo($('body'));
}
//要加载和执行的脚本数组
var scriptsToExecute=[
"http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js?t=“+Date.now(),
"http://ajax.googleapis.com/ajax/libs/chrome-frame/1.0.3/CFInstall.min.js?t=“+Date.now()
];
//加载的脚本存储在这里
var loadedScripts={};
//出于演示目的,请求按相反顺序发送。
//它们仍将按照数组中指定的顺序执行。
(函数开始(){
对于(var i=scriptsToExecute.length-1;i>=0;i--){
(功能(){
var addr=scriptsToExecute[i];
请求数据(地址,函数(){
控制台日志(“已加载”+addr);
});
})();
}
})();
//此函数执行当前可以执行的尽可能多的脚本
//插入具有相应src属性的脚本标记。这个
//脚本不会重新加载,因为它们在缓存中。你可以
//或者eval`script.code`
函数executeScript(脚本){
loadedScripts[script.URL]=script.code
while(loadedScripts.hasOwnProperty(scriptsToExecute[0])){
var scriptToRun=scriptsToExecute.shift()
var-element=document.createElement('script');
setAttribute(“type”,“text/javascript”);
setAttribute(“src”,scriptToRun);
$('head')。追加(元素);
console.log(“已执行”+脚本运行);
}
}
//此函数用于发出脚本请求
函数requestData(路径、加载调用
function loadScript(order, path) {
    var xhr = new XMLHttpRequest();
    xhr.open("GET",path,true);
    xhr.send();
    xhr.onreadystatechange = function(){
        if(xhr.readyState  == 4){
            if(xhr.status >= 200 && xhr.status < 300 || xhr == 304){
                loadedScripts[order] = xhr.responseText;
            }
            else {
                //deal with error
                loadedScripts[order] = 'alert("this is a failure to load script '+order+'");';
                // or  loadedScripts[order] = '';  // this smoothly fails
            }
            alert(order+' - '+xhr.status+' > '+xhr.responseText);                // this is to show the completion order.  Careful, FF stacks aletrs so you see in reverse.
            // am I the last one ???
            executeAllScripts();
        }
    };
}

function executeAllScripts(){
    if(loadedScripts.length!=scriptsToLoad.length) return;
    for(var a=0; a<loadedScripts.length; a++) eval(loadedScripts[a]);
    scriptsToLoad = [];
}


var loadedScripts = [];
var scriptsToLoad = [
   "http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.min.js",
   "http://ajax.googleapis.com/ajax/libs/chrome-frame/1.0.3/CFInstall.min.js",
   "http://nowhere.existing.real_script.com.ar/return404.js"
];

// load all   even in reverse order ... or randomly
for(var a=0; a<scriptsToLoad.length; a++) loadScript(a, scriptsToLoad[a]);
var stack = [];
stack.loaded = 0;
function loadScriptNew(path, callback) {
        var o = { callback: callback };
        stack.push(o);

        loadScript(path, function() {
                o.callbackArgs = arguments;
                stack.loaded++;
                executeWhenReady();
        });
}

function executeWhenReady() {
        if ( stack.length == stack.loaded ) {
                while(stack.length) {
                        var o = stack.pop();
                        o.callback.apply(undefined, o.callbackArgs);
                }

                stack.loaded = 0;
        }
}
function loadScript(path, callback) {
        var element = document.createElement('script');
        element.setAttribute("type", 'text/javascript');
        element.setAttribute("src", path);

        return loadElement(element, callback);
}

function loadElement(element, callback) {
        element.setAttribute("defer", "");
        // element.setAttribute("async", "false");

        element.loaded = false;

        if (element.readyState){  // IE
                element.onreadystatechange = function(){
                        if (element.readyState == "loaded" || element.readyState == "complete"){
                                element.onreadystatechange = null;

                                loadElementOnLoad(element, callback);
                        }
                };
        } else {                 // Others
                element.onload = function() {
                        loadElementOnLoad(element, callback);
                };
        }

        (document.head || document.getElementsByTagName('head')[0] || document.body).appendChild(element);

        return element;
}

function loadElementOnLoad(element, callback) {
        if (element.loaded != true) {
                element.loaded = true;
                if ( callback ) callback(element);
        }
}

loadScriptNew("http://ajax.googleapis.com/ajax/libs/angularjs/1.0.7/angular.js",function() {
        alert(1);
});

loadScriptNew("http://ajax.googleapis.com/ajax/libs/chrome-frame/1.0.3/CFInstall.min.js",function() {
        alert(2);
});