如何让JavaScript代码执行等待带有脚本的AJAX请求加载并执行?

17

在我的应用程序中,我使用Ext.Ajax.request来加载脚本,并使用eval执行它们。

问题是由于 AJAX 请求需要时间才能完成,在 AJAX 加载的脚本中定义的变量需要在之后执行的代码中使用。在这个例子中,我展示了这种情况。如何更改此代码,使得在 AJAX 请求加载并执行脚本之后才执行 JavaScript 代码?

testEvalIssue_script.htm:

<script type="text/javascript">
    console.log('2. inside the ajax-loaded script');
</script>

main.htm:

<html>
    <head>
        <script type="text/javascript" src="ext/adapter/ext/ext-base.js"></script>
        <script type="text/javascript" src="ext/ext-all-debug.js"></script>
        <script type="text/javascript">
            function loadViewViaAjax(url) {
                Ext.Ajax.request({
                    url: url,
                    success: function(objServerResponse) {
                        var responseText = objServerResponse.responseText;
                        var scripts, scriptsFinder=/<script[^>]*>([\s\S]+)<\/script>/gi;
                        while(scripts=scriptsFinder.exec(responseText)) {
                            eval.call(window,scripts[1]);
                        }
                    }
                });
            }

            console.log('1. before loading ajax script');
            loadViewViaAjax('testEvalIssue_script.htm');
            console.log('3. after loading ajax script');
        </script>
    </head>
    <body>

    </body>

</html>

输出:

1. before loading ajax script
3. after loading ajax script
2. inside the ajax-loaded script

我该如何使输出按照正确的顺序呈现,就像这样:

1. before loading ajax script
2. inside the ajax-loaded script
3. after loading ajax script

4
把2放到3的位置,把3放到2的位置。 - Gilbert Le Blanc
在 AJAX 请求完成后调用一个函数,并向用户显示“正在加载...”消息。 - Kevin
4个回答

13

Ajax是异步的,这意味着当Ajax调用被分派时,您的代码会继续运行,而不会停止。 Ajax不会暂停执行,直到收到响应。 您需要添加额外的回调函数或类似内容。

<html>
    <head>
        <script type="text/javascript" src="ext/adapter/ext/ext-base.js"></script>
        <script type="text/javascript" src="ext/ext-all-debug.js"></script>
        <script type="text/javascript">
            function loadViewViaAjax(url, callback) {
                Ext.Ajax.request({
                    url: url,
                    success: function(objServerResponse) {
                        var responseText = objServerResponse.responseText;
                        var scripts, scriptsFinder=/<script[^>]*>([\s\S]+)<\/script>/gi;
                        while(scripts=scriptsFinder.exec(responseText)) {
                            eval.call(window,scripts[1]);
                        }
                        callback.call();
                    }
                });
            }

            console.log('1. before loading ajax script');
            var afterAjax = function(){
                console.log('3. after loading ajax script');
            }
            loadViewViaAjax('testEvalIssue_script.htm', afterAjax);
        </script>
    </head>
    <body>

    </body>

</html>

3
由于 ajax 调用是异步的,如果您想执行依赖通过 ajax 加载的数据的操作,则必须在 success 方法中执行它。将代码放在另一个方法中,然后在 eval 语句之后调用该方法。
<script type="text/javascript">
            function doSomeAmazingThings() {
                // amazing things go here
            }

            function loadViewViaAjax(url) {
                Ext.Ajax.request({
                    url: url,
                    success: function(objServerResponse) {
                        var responseText = objServerResponse.responseText;
                        var scripts, scriptsFinder=/<script[^>]*>([\s\S]+)<\/script>/gi;
                        while(scripts=scriptsFinder.exec(responseText)) {
                            eval.call(window,scripts[1]);
                        }
                        doSomeAmazingThings(); 
                        console.log('3. after loading ajax script');
                    }
                });
            }

            console.log('1. before loading ajax script');
            loadViewViaAjax('testEvalIssue_script.htm');
        </script>

1
是的,但需要明确的是,您确实应该将第三个控制台语句移动到回调函数中或者移到您的新伪造函数中。如所示,对于不理解问题的人来说仍然相当困惑。 - Brian Moeskau

1

你可以尝试将AJAX调用设置为同步的...当然,你必须停止使用Ext.Ajax库,但如果你需要在下一行代码之前获得ajax结果,这是值得的。

这就是我的内部网页与数据库通信的方式。我发现了一个问题,就是在Javascript将控制权交还给页面之前,你无法看到任何页面更新,因此像状态栏、进度条或覆盖层这样的东西在同步Ajax中是不可能的(对于我的Firefox来说并非如此,在某些情况下它甚至可以在同步代码内部更新)。

我使用的是这个方法——它有点自己编写和混乱,但在我的圈子里已经完美地工作了多年。只需创建一个新的AJAX(),设置URL,根据需要添加查询(名称/值对),将异步设置为false,当你从函数调用Execute时,它将阻塞直到Ajax返回。

或者,如果你想异步使用它,只需为你创建的AJAX对象编写一个新的“onready”函数,并将异步更改为true。

我很多年前就写了这个,所以它不是最整洁的,也有其他方法可以做事情,但它作为一个入门指南是有效的,你可以根据自己的喜好进行微调,而不必依赖于另一个库。

function AJAX(){

//Declarations
    var thisExt=this; //To be referenced by events

//Initialize Properties
    thisExt.URL="";
    thisExt.Query="";
    thisExt.Method="GET";
    thisExt.Asynchronous=true;
    thisExt.Encoding="application/x-www-form-urlencoded";
    thisExt.PostData="";

// Provide the XMLHttpRequest class for IE 5.x-6.x:
// Other browsers (including IE 7.x-8.x) ignore this
//   when XMLHttpRequest is predefined
if (typeof XMLHttpRequest == "undefined") {
    try { 
        thisExt.XMLHTTP = new ActiveXObject("Msxml2.XMLHTTP.6.0"); 
    }
    catch (e1) {
        try { thisExt.XMLHTTP = new ActiveXObject("Msxml2.XMLHTTP.3.0"); }
        catch (e2) {
            try { thisExt.XMLHTTP = new ActiveXObject("Msxml2.XMLHTTP"); }
            catch (e3) {
                try { thisExt.XMLHTTP = new ActiveXObject("Microsoft.XMLHTTP"); }
                catch (e4) {
                    throw new Error("This browser does not support XMLHttpRequest.");
                }
            }
        }
    }
} else {
    thisExt.XMLHTTP = new XMLHttpRequest();
}


//Methods
    thisExt.XMLHTTP.onreadystatechange = function(){
        if(thisExt.XMLHTTP.readyState==4){
            window.status="";
            thisExt.onready(thisExt);//Passes thisExt so that the callback will have assess to the entire object, not just the returned text.
        }else{
            window.status=thisExt.XMLHTTP.readyState;//Just for debugging
        }
    }
    thisExt.addQuery=function(name,value){
        if(thisExt.Query!=""){
            thisExt.Query+="&"
        }
        thisExt.Query+=encodeURIComponent(name)+"="+encodeURIComponent(value)
    }

//Not really necessary, you could just say AjaxObj.URL="bla bla"
    thisExt.setURL=function(URL){
        thisExt.URL=URL;
    }
//Not really necessary, you could just say AjaxObj.Query="bla bla"
    thisExt.setQuery=function(Query){
        thisExt.Query=Query;
    }
//Not really necessary, you could just say AjaxObj.Method="bla bla"
    thisExt.setMethod=function(Method){
        thisExt.Method=Method;
    }
//Not really necessary, you could just say AjaxObj.Encoding="bla bla"
    thisExt.setEncoding=function(Encoding){
        thisExt.Encoding=Encoding;
    }
//Not really necessary, you could just say AjaxObj.PostData="bla bla"
    thisExt.setPostData=function(PostData){
        thisExt.PostData=PostData;
    }

    thisExt.Execute=function(){
        if(thisExt.URL==""){
            alert("AJAX.URL cannot be null.")
            return;
        }
        var URL2=thisExt.URL;
        if(thisExt.Query!=""){
            URL2=URL2+"?"+thisExt.Query;

        }
        if(thisExt.Method=="POST"){
            //this.XMLHTTP.setRequestHeader("Content-Type",this.Encoding);
            thisExt.XMLHTTP.open("POST", URL2, thisExt.Asynchronous);
            thisExt.XMLHTTP.send(thisExt.PostData);
        } else {
            thisExt.XMLHTTP.open("GET", URL2, thisExt.Asynchronous);
            thisExt.XMLHTTP.send(null);
        }
    }

//Events & callbacks
    thisExt.onready=function(){}
}

1
这可以通过创建一个XMLHttpRequest对象并调用open函数,将async参数设置为false来实现。
var xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", "ajax_info.txt", false);
var response = xmlhttp.responseText;

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接