Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/479.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
同步动态加载JavaScript_Javascript - Fatal编程技术网

同步动态加载JavaScript

同步动态加载JavaScript,javascript,Javascript,我正在使用,我想做的事情之一是动态地包含一个外部JavaScript文件,执行该文件,然后在模块的return{}中使用文件中的函数/变量 我想不出怎么容易做到这一点。是否有执行伪同步外部脚本加载的标准方法 function myModule() { var tag = document.createElement("script"); tag.type = "text/javascript"; tag.src = "http://some/script.js";

我正在使用,我想做的事情之一是动态地包含一个外部JavaScript文件,执行该文件,然后在模块的
return{}
中使用文件中的函数/变量

我想不出怎么容易做到这一点。是否有执行伪同步外部脚本加载的标准方法

function myModule() {
    var tag = document.createElement("script");
    tag.type = "text/javascript";
    tag.src = "http://some/script.js";
    document.getElementsByTagName('head')[0].appendChild(tag);

    //something should go here to ensure file is loaded before return is executed

    return {
        external: externalVariable 
    }
}
由于明显的原因,您不能也不应该同步执行服务器操作。不过,您可以做的是让事件处理程序在加载脚本时告诉您:

tag.onreadystatechange = function() { if (this.readyState == 'complete' || this.readyState == 'loaded') this.onload({ target: this }); };

tag.onload = function(load) {/*init code here*/}

onreadystatechange
委派是IE的一种内存解决方案,它对
onload

只有一种方法可以同步加载和执行脚本资源,那就是使用同步XHR

这是一个如何做到这一点的例子

// get some kind of XMLHttpRequest
var xhrObj = createXMLHTTPObject();
// open and send a synchronous request
xhrObj.open('GET', "script.js", false);
xhrObj.send('');
// add the returned content to a newly created script tag
var se = document.createElement('script');
se.type = "text/javascript";
se.text = xhrObj.responseText;
document.getElementsByTagName('head')[0].appendChild(se);
但一般来说,您不应该使用同步请求,因为这将阻止其他所有操作。 但尽管如此,当然也存在这样的情况

我可能会使用onload处理程序将包含函数重构为异步模式。

我使用应用于div元素的jquery加载方法。差不多

<div id="js">
<!-- script will be inserted here --> 
</div>

...

$("#js").load("path", function() {  alert("callback!" });

...
$(“#js”).load(“path”,function(){alert(“callback!”});

您可以多次加载脚本,每次一个脚本将完全替换先前加载的脚本,与Sean的答案相同,但不创建脚本标记,只需对其进行评估。这确保代码实际上可以使用。

这是我在应用程序中用于多文件加载的代码

Utilities.require = function (file, callback) {
    callback = callback ||
    function () {};
    var filenode;
    var jsfile_extension = /(.js)$/i;
    var cssfile_extension = /(.css)$/i;

    if (jsfile_extension.test(file)) {
        filenode = document.createElement('script');
        filenode.src = file;
        // IE
        filenode.onreadystatechange = function () {
            if (filenode.readyState === 'loaded' || filenode.readyState === 'complete') {
                filenode.onreadystatechange = null;
                callback();
            }
        };
        // others
        filenode.onload = function () {
            callback();
        };
        document.head.appendChild(filenode);
    } else if (cssfile_extension.test(file)) {
        filenode = document.createElement('link');
        filenode.rel = 'stylesheet';
        filenode.type = 'text/css';
        filenode.href = file;
        document.head.appendChild(filenode);
        callback();
    } else {
        console.log("Unknown file type to load.")
    }
};

Utilities.requireFiles = function () {
    var index = 0;
    return function (files, callback) {
        index += 1;
        Utilities.require(files[index - 1], callBackCounter);

        function callBackCounter() {
            if (index === files.length) {
                index = 0;
                callback();
            } else {
                Utilities.requireFiles(files, callback);
            }
        };
    };
}();
这个实用程序可以由

Utilities.requireFiles(["url1", "url2",....], function(){
    //Call the init function in the loaded file.
    })

如果您需要加载任意数量的脚本,并且只在最后一个脚本加载完成时才继续,并且您不能使用XHR(例如,由于CORS限制),则可以执行以下操作。它不是同步的,但允许在最后一个文件加载完成时进行回调:

// Load <script> elements for all uris
// Invoke the whenDone callback function after the last URI has loaded
function loadScripts(uris,whenDone){
  if (!uris.length) whenDone && whenDone();
  else{
    for (var wait=[],i=uris.length;i--;){
      var tag  = document.createElement('script');
      tag.type = 'text/javascript';
      tag.src  = uris[i];
      if (whenDone){
        wait.push(tag)
        tag.onload = maybeDone; 
        tag.onreadystatechange = maybeDone; // For IE8-
      }
      document.body.appendChild(tag);
    }
  }
  function maybeDone(){
    if (this.readyState===undefined || this.readyState==='complete'){
      // Pull the tags out based on the actual element in case IE ever
      // intermingles the onload and onreadystatechange handlers for the same
      // script block before notifying for another one.
      for (var i=wait.length;i--;) if (wait[i]==this) wait.splice(i,1);
      if (!wait.length) whenDone();
    }
  }
}
//加载所有URI的元素
//在加载最后一个URI后调用whenDone回调函数
函数加载脚本(URI,whenDone){
如果(!uri.length)whenDone&&whenDone();
否则{
for(var wait=[],i=uris.length;i--;){
var tag=document.createElement('script');
tag.type='text/javascript';
tag.src=uris[i];
如果(何时){
等等,推(标签)
tag.onload=maybeDone;
tag.onreadystatechange=maybeDone;//对于IE8-
}
document.body.appendChild(标签);
}
}
函数maybeDone(){
if(this.readyState===未定义| | this.readyState====完成){
//根据实际元素拉出标签,以防IE出现
//混合同一对象的onload和onreadystatechange处理程序
//在通知另一个脚本块之前执行脚本块。
for(vari=wait.length;i--;)if(wait[i]==this)wait.splice(i,1);
如果(!wait.length)whenDone();
}
}
}
编辑:更新为与IE7、IE8和IE9一起使用(在怪癖模式下)。这些IE版本不会触发
onload
事件,但会触发
onreadystatechange
事件。标准模式下的IE9会同时触发这两个事件(对于任何脚本,在
onload
之前触发的所有脚本,使用
onreadystatechange

基于此,IE的旧版本很可能永远不会发送带有
readyState=='complete'
onreadystatechange
事件;如果是这种情况(我无法重现此问题),则上述脚本将失败,并且您的回调将永远不会被调用。

错误不正确

同步加载文件与同步执行文件不同——这是OP所要求的

接受的答案会加载文件同步,但只会向DOM追加一个脚本标记。仅仅因为appendChild()已返回,并不保证脚本已完成执行,并且其成员已初始化以供使用

实现OPs问题的唯一(请参见警告)方法是,按照说明通过XHR同步加载脚本,然后作为文本读取并传递到eval()或新函数()调用中,然后等待该函数返回。这是确保脚本同步加载和执行的唯一方法

无论是从UI还是从安全性角度来看,我都不会评论这是否是一件明智的事情,但肯定有一些用例证明了同步加载和执行的合理性

警告:
除非您使用的是web workers,在这种情况下,只需调用loadScripts();

我对这个问题的现有答案(以及其他stackoverflow线程上这个问题的变体)有以下问题:

  • 加载的代码都不可调试
  • 许多解决方案都需要回调来知道加载何时完成,而不是真正的阻塞,这意味着立即调用加载(ie加载)代码会导致执行错误
或者更准确地说:

  • 加载的代码都不可调试(HTML脚本标记块除外,如果且仅当解决方案将脚本元素添加到dom中时,并且永远不会作为单个可查看脚本。)=>考虑到我必须加载(和调试)的脚本数量,这是不可接受的
  • 使用“onreadystatechange”或“onload”事件的解决方案无法阻止,这是一个大问题,因为代码最初使用“require([filename,'dojo/domReady']);”同步加载动态脚本,而我正在剥离dojo
我的最终解决方案是在返回之前加载脚本,并在调试器中正确访问所有脚本(至少对于Chrome),如下所示:

警告:以下代码可能只能在“开发”模式下使用。(对于“发布”模式,我建议在不加载动态脚本或至少不使用eval的情况下进行预打包和缩小)

//代码用户TODO:您必须创建并设置自己的
//Code User TODO: you must create and set your own 'noEval' variable

require = function require(inFileName)
{
    var aRequest
        ,aScript
        ,aScriptSource
        ;

    //setup the full relative filename
    inFileName = 
        window.location.protocol + '//'
        + window.location.host + '/'
        + inFileName;

    //synchronously get the code
    aRequest = new XMLHttpRequest();
    aRequest.open('GET', inFileName, false);
    aRequest.send();

    //set the returned script text while adding special comment to auto include in debugger source listing:
    aScriptSource = aRequest.responseText + '\n////# sourceURL=' + inFileName + '\n';

    if(noEval)//<== **TODO: Provide + set condition variable yourself!!!!**
    {
        //create a dom element to hold the code
        aScript = document.createElement('script');
        aScript.type = 'text/javascript';

        //set the script tag text, including the debugger id at the end!!
        aScript.text = aScriptSource;

        //append the code to the dom
        document.getElementsByTagName('body')[0].appendChild(aScript);
    }
    else
    {
        eval(aScriptSource);
    }
};
var xhrObj = new XMLHttpRequest();
xhrObj.open('GET', '/filename.js', false);
xhrObj.send(null);
eval(xhrObj.responseText);
$.getScript('/filename.js',callbackFunction);
app.provider('SomeScriptSyncLoader', function() {

    var resourceUrl =  'http://some/script.js';
    var dummy = {};

    this.$get = function() {

        var q = jQuery.ajax({
            type: 'GET', url: resourceUrl, cache: false, async: false
        });

        if (q.status === 200) {
            eval(q.responseText); // execute some script synchronously as inline script - eval forces sync processing
        }
        return dummy;
    };
});
app.directive('myDirective', ['SomeScriptSyncLoader', function(someScriptSyncLoader) {

return {
    restrict: 'E',
    link: function(scope, element, attrs) {
        // some ode
    },
    template: "this is my template"
   };
}]);
var scriptCache = [];
var paths = [];
function Import(path)
{
    var index = 0;
    if((index = paths.indexOf(path)) != -1) //If we already imported this module
    {
        return scriptCache [index];
    }

    var request, script, source;
    var fullPath = window.location.protocol + '//' + window.location.host + '/' + path;

    request = new XMLHttpRequest();
    request.open('GET', fullPath, false);
    request.send();

    source = request.responseText;

    var module = (function concealedEval() {
        eval(source);
        return exports;
    })();

    scriptCache.push(module);
    paths.push(path);

    return module;
}
function AddTwoObjects(a, b)
{
    return a + b;
}

this.exports = AddTwoObjects;
var AddTwoObjects = Import('addobjects.js');
alert(AddTwoObjects(3, 4)); //7
//or even like this:
alert(Import('addobjects.js')(3, 4)); //7
- directory
---- index.html
---- bundle.js
---- test_module/
-------- a.js
-------- b.js
-------- log_num.js
-------- many_parameters.js
<head>
  <script src="bundle.js"></script>
</head>
// Give JS arrays the .empty() function prototype
if (!Array.prototype.empty){
    Array.prototype.empty = function(){
        return this.length == 0;
    };
};

function bundle(module_object, list_of_files, directory="") {
  if (!list_of_files.empty()) {
    var current_file = list_of_files.pop()
    var [function_name, extension] = current_file.split(".")
    var new_script = document.createElement("script")
    document.head.appendChild(new_script)

    new_script.src = directory + current_file

    new_script.onload = function() {
      module_object[function_name] = eval(function_name)
      bundle(module_object, list_of_files, directory)
      /*
      nullify the function in the global namespace as - assumed -  last
      reference to this function garbage collection will remove it. Thus modules
      assembled by this function - bundle(obj, files, dir) - must be called
      FIRST, else one risks overwritting a funciton in the global namespace and
      then deleting it
      */
      eval(function_name + "= undefined")
    }
  }
}

var test_module = {}
bundle(test_module, ["a.js", "b.js", "log_num.js", "many_parameters.js"], "test_module/")
function a() {
  console.log("a")
}
function b() {
  console.log("b")
}
// it works with parameters too
function log_num(num) {
  console.log(num)
}
function many_parameters(a, b, c) {
  var calc = a - b * c
  console.log(calc)
}
script.async = false;
<script>
(function() {
  var scriptNames = [
    "https://code.jquery.com/jquery.min.js",
    "example.js"
  ];
  for (var i = 0; i < scriptNames.length; i++) {
    var script = document.createElement('script');
    script.src = scriptNames[i];
    script.async = false; // This is required for synchronous execution
    document.head.appendChild(script);
  }
  // jquery.min.js and example.js will be run in order and synchronously
})();
</script>

<!-- Gotcha: these two script tags may still be run before `jquery.min.js`
     and `example.js` -->
<script src="example2.js"></script>
<script>/* ... */<script>
    function loadScript(url) {
      return new Promise((resolve, reject) => {
        var script = document.createElement('script')
        script.src = url
        script.onload = () => {
          resolve()
        }
        script.onerror = () => {
          reject('cannot load script '+ url)
        }
        document.body.appendChild(script)
      })
    }
        loadScript('myfirstscript.js').then(() => {
          console.log('first script ran');
          loadScript('index.js').then(() => {
            console.log('second script ran');
          })
        })
var loaded_script = [];
function loadScript(urls, callback, sync) {
    var len = urls.length, count = 0;

    // check are all js loaded, then execute callback (if any)
    var check = function() {
        if (count == len) {
            callback && typeof callback=="function" && callback();
        }
    };

    for (var i = 0; i < len; i++) {
        var url = urls[i];

        // check if script not loaded (prevent load again)
        if (loaded_script.indexOf(url) == -1) {
            var script = document.createElement("script");
            script.type = "text/javascript";

            // set sync loading here (default is async)
            if (sync) {
                script.async = false;
            }

            // script onload event
            if (script.readyState) {    // IE
                script.onreadystatechange = function() {
                    if (script.readyState=="loaded" || script.readyState=="complete") {
                        script.onreadystatechange = null;
                        count++, check();
                    }
                };
            } else {    // Others
                script.onload = function() {
                    count++, check();
                };
            }

            // add script to head tag
            script.src = url;
            document.getElementsByTagName("head")[0].appendChild(script);

            // mark this script has loaded
            loaded_script.push(url);
        } else {
            count++, check();
        }
    }
}
loadScript(
    [
        "js/first.js",
        "js/second.js",
    ],
    function() {
        alert("Scripts loaded.");
    },
    true
);
// Loads a script or an array of scripts (including stylesheets)
// in their respective index order, synchronously.
// By Sayanjyoti Das @https://stackoverflow.com/users/7189950/sayanjyoti-das
var Loader={
    queue: [], // Scripts queued to be loaded synchronously
    loadJsCss: function(src, onl) {
        var ext=src.toLowerCase().substring(src.length-3, src.length);
        if(ext=='.js') {
            var scrNode=el('script', null, null, null);
            scrNode.type='text/javascript';
            scrNode.onload=function() {onl();};
            scrNode.src=src;
            document.body.appendChild(scrNode);
        }else if(ext=='css') {
            var cssNode=el('link', null, null, null);
            cssNode.rel='stylesheet';
            cssNode.type='text/css';
            cssNode.href=src;
            document.head.appendChild(cssNode);
            onl();
        }
    },
    add: function(data) {
        var ltype=(typeof data.src).toLowerCase();

        // Load a single script
        if(ltype=='string') {
            data.src=data.src;
            Loader.queue.splice(0, 1, data, Loader.queue[0]);
            Loader.next();
        }
        // Load an array of scripts
        else if(ltype=='object') {
            for(var i=data.src.length-1; i>=0; i--) {
                Loader.queue.splice(0, 1, {
                    src: data.src[i],
                    onload: function() {
                        if(Loader.next()==false) {
                            data.onload();
                            return;
                        }
                        Loader.next();
                    }
                }, Loader.queue[0]);
            }
            Loader.next();
        }
    },
    next: function() {
        if(Loader.queue.length!=0 && Loader.queue[0]) {
            var scr=Loader.queue[0];

            // Remove the script from the queue
            if(Loader.queue.length>1)
                Loader.queue.splice(0, 2, Loader.queue[1]);
            else
                Loader.queue=[];

            // Load the script
            Loader.loadJsCss(scr.src, scr.onload);
        }else return false;
    }
};
// Load a single script
Loader.add({
    src: 'test.js',
    onload: function() {
        alert('yay!');
    }
});

// Load multiple scripts
Loader.add({
    src: ['test1.js', 'test2.js', 'mystyles.css', 'test3.js'],
    onload: function() {
        alert('all loaded!');
    }
});