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