延期时间问题

时间:2014-08-19 21:04:47

标签: javascript jquery ajax promise

我有以下代码,我的目的是在运行时加载和运行脚本。您注意到,如果它已经存在,我会将其保存到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();
}

2 个答案:

答案 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属性,让浏览器为您执行加载。您可以看到几种方法herehere

答案 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输出。

也许能够更好地掌握技术细节的人可以澄清。现在我很高兴停止撕掉我的头发,但我想知道事情是如何起作用的......