我有以下代码,我的目的是在运行时加载和运行脚本。您注意到,如果它已经存在,我会将其保存到localStorage。现在它运行正常,如果它已经存储在那里,但是当它刚从文件中获取文本时它会抛出ReferenceError: loginLaunch is not defined
,尽管文本似乎已被加载(因此{{1}检查长度的行)。为方便起见,我添加了一行console.log
,使其在问题的错误消息和localStorage.clear();
之间切换,给出下面的代码是所需的结果。
我不明白为什么它应该以一种方式而不是另一种方式工作。如果这是一个时间问题,我不会看到使用承诺ReferenceError: loginLaunch is not defined
如何让它通过,除非可能loginCode
是异步的,但我在印象中它不是(主要是因为它没有回调,我试图找出,但不能),甚至为什么在appendChild()
之前编码会产生影响?
我搞砸了其中一项承诺吗?我在最后包含了文件login.js的内容。我搜索过任何相关的东西,但没有任何运气,只有一篇帖子说明appendChild()
是同步的。
请帮忙。
appendChild
这里是login.js文件的内容。
var loginCode = runCode("login_1001","./js/login.js");
loginCode.done(loginLaunch());
//FUNCTIONS START HERE
function getCode(local, source) { //This creates the promise to get the code (not to run it)
console.log("start of loadCode");
dfd = $.Deferred(); //This is the one to return.
script = localStorage.getItem(local); //Try to load from local storage.
// console.log("script after local attempt: "+script);
if (script) { //If found...
console.log("found Local code");
dfd.resolve(script);
localStorage.clear(); //Added for debugging
} else { //load from file.
ajax = $.ajax({
url : source,
cache : false,
dataType : "text", //load as text initially so that we can store it locally.
});
ajax.done(function(fromFile){
localStorage.setItem(local, fromFile); //store it locally.
//console.log("script after ajax attempt: "+script);
dfd.resolve(fromFile);
});
ajax.fail(function(){
dfd.reject("Error retrieving code. You may be disconnected");
});
}
return dfd.promise();
}
function runCode(local, source) {
dfd = $.Deferred(); //This is the one to return.
code = getCode(local, source); //local promise
code.done(function(retrievedCode){
console.log(retrievedCode.length);
var head = document.getElementsByTagName('head')[0]; //first head section
var el = document.createElement("script"); //named the same as the local storage
//script.type= 'text/javascript'; Redundant — it's the default
// el.id = local; //Probably redundant, but if we want to manipulate it later...
el.text = retrievedCode;
head.appendChild(el); //This shouldn't run anything, just make global functions that will be called later.
console.log(el.text.length);
dfd.resolve(); //If we need to return the node to manipulate it later we'd make the variable above and 'return' it here
});
return dfd.promise();
}
答案 0 :(得分:0)
你需要改变至少三件事。首先改变这个:
loginCode.done(loginLaunch());
到此:
loginCode.done(function() {loginLaunch()});
您需要将函数引用传递给.done()
处理程序,以便稍后调用它。你拥有它的方式,你在loginCode()
完成其工作之前就立即调用它,因此它被调用得太早了。
此外,loginLaunch
尚未存在,因此您无法直接向其传递引用。相反,您可以将引用传递给包装函数,然后在最终存在之后调用loginLaunch()
。
其次,你需要使用var
声明你的局部变量,这样它们就不会是隐式全局变量并相互踩踏。例如,您有多个函数相互调用,试图使用相同的全局dfd
。这是灾难的秘诀。将var
放在它前面以使其成为局部变量,因此它对该范围是唯一的。
第三,el.text
看起来对我的脚本来说不是正确的属性。也许你打算使用.textContent
或者因为你有jQuery,你可以这样做:
$(el).text(retrievedCode);
在一个与样式相关的问题中,所有局部变量都应该在它们之前用var
声明,因此它们不是隐式全局变量。这将通过引起神秘的,难以追踪的错误来扼杀你,甚至更多地使用异步代码。
并且,您通常可以使用jQuery从ajax函数返回的promise,而不是创建自己的。
将这些改进纳入其中:
runCode("login_1001","./js/login.js").done(loginLaunch);
function getCode(local, source) { //This creates the promise to get the code (not to run it)
var script = localStorage.getItem(local); //Try to load from local storage.
if (script) { //If found...
localStorage.clear(); //Added for debugging
// return a resolved promise (since there's no async here)
return $.Deferred().resolve(script);
} else { //load from file.
// return the ajax promise
return $.ajax({
url : source,
cache : false,
dataType : "text", //load as text initially so that we can store it locally.
}).then(function(fromFile){
localStorage.setItem(local, fromFile); //store it locally.
return fromFile;
});
}
}
function runCode(local, source) {
return getCode(local, source).then(function(retrievedCode){
console.log(retrievedCode.length);
var head = document.getElementsByTagName('head')[0]; //first head section
var el = document.createElement("script"); //named the same as the local storage
$(el).text(retrievedCode);
head.appendChild(el); //This shouldn't run anything, just make global functions that will be called later.
console.log(el.text.length);
});
}
仅供参考,如果你只是想插入一个脚本文件,你不必自己用ajax手动检索脚本。您可以在脚本标记上使用src
属性,让浏览器为您执行加载。您可以看到几种方法here和here。
答案 1 :(得分:0)
我不确定为什么会这样:
var loginCode = runCode("login_1001","./js/login.js");
loginCode.done(function(){loginLaunch();});
并且没有:
var loginCode = runCode("login_1001","./js/login.js");
loginCode.done(loginLaunch);
我的一个想法是,如果你将文字命名函数传递给.done
,那么在创建loginCode时它们会被验证,而匿名函数在它们即将运行之前不会被验证。
我应该注意,错误出现在之前 console.log输出。
也许能够更好地掌握技术细节的人可以澄清。现在我很高兴停止撕掉我的头发,但我想知道事情是如何起作用的......