3

我正在“学习 PHP、MySQL 和 JavaScript -O'Reilly”中学习一些非常基本的 AJAX 编程。有几个非常基本的 Java 程序使用 AJAX 获取一些文本或页面。我正在使用 W3Schools.com 站点来尝试了解有关使用 AJAX 获取 XML 格式文档的更多信息。我有两个与我正在学习的内容有关的问题。

当我使用来自http://www.w3schools.com/dom/dom_loadxmldoc.asp的代码时,我可以返回一个带有以下片段的 XML 文档:

function loadXMLDoc(dname)
{
if (window.XMLHttpRequest)
  {
  xhttp=new XMLHttpRequest();
  }
else
  {
  xhttp=new ActiveXObject("Microsoft.XMLHTTP");
  }
xhttp.open("GET",dname,false);
xhttp.send();
return xhttp.responseXML;
}

//called this way
xmlDoc=loadXMLDoc("books.xml");

但是 async 必须设置为 false 才能工作。如果我将 async 设置为 true,它会返回 null。所以我的第一个问题是为什么 async 必须为 false 才能返回 XML 文档?

我的第二个问题,当我尝试将书中的一些代码和网站上的一些代码结合起来时,片段如下:

<body>API Call output
<div id='output'>will be here</div>
<script>
out = ""
xmlDoc = getResponse()
users = xmlDoc.getElementsByTagName('username')
for (count = 0; count < users.length ; count++)
{
    out += "Username: " + users[count].childNodes[0].nodeValue + '<br />'
}
document.getElementById('output').innerHTML = out

function ajaxRequest()
{
    try //good browser
    {
        var request = new XMLHttpRequest()
    }
    catch(e1)
    {
        try // IE6+
        {
            request = new ActiveXObject("Msxml2.XMLHTTP")
        }
        catch(e2)
        {
            request = false
        }
    }//end catch(e1)
    return request
}

function getResponse()
{
    params = "apikey=test"
    request = new ajaxRequest()
    request.open("POST", "processapi.php", true)
    request.setRequestHeader("Content-type",
        "application/x-www-form-urlencoded")
    request.setRequestHeader("Content-length", params.length)
    request.setRequestHeader("Connection", "close")
    request.onreadystatechange = function()
    {
        if (this.readyState == 4)
        {
            if (this.status == 200)
            {
                if (this.responseXML != null)
                {
                    return this.responseXML
                }
                else alert("Ajax error: No data received")
            }
            else alert( "Ajax error: " + this.statusText)
        }
    }

    request.send(params)
}
</script>
</body>

我收到“TypeError:xmlDoc 未定义”的错误。在异步设置为 true 或 false 的函数 getResponse() 中它不起作用,我得到 xmlDoc is undefined 错误。在将 xmlDoc 设置为 loadXMLDoc() 的返回之前,在第一个示例(来自 w3schools.com)的其他地方没有定义 xmlDoc,并且效果很好。ajaxRequest() 不在书中,getResponse() 中的大部分内容都在书中,但我把它放在了 getResponse() 函数中。从此脚本调用时,processapi.php 只是回显一个 XML 文档。

为什么 async 在第一个示例中不起作用,为什么 xmlDoc 在第二个示例中未定义?任何帮助是极大的赞赏。

4

2 回答 2

0

您的两个问题的答案在于异步的本质。随着async存在true,这将返回null

xhttp.open("GET",dname,false);
xhttp.send();
return xhttp.responseXML;

...因为在语句发生时 HTTP 请求尚未完成return,因此xhttp.responseXML仍然具有其默认值 ( null)。HTTP 请求稍后在该代码流之外完成(异步 - 字面意思,不是同步)。

您的getResponse函数根本不会返回值。您分配给的匿名回调函数onreadystatechange返回一个值,但这绝不会影响getResponse.

客户端 Web 编程的关键方面之一是接受环境的异步、事件驱动性质。几乎没有用于设置 XHR 请求的用asyncfalse。相反,习惯于对事物使用回调。例如,您的getResponse函数可能如下所示:

// ***CHANGE*** -----v--- Accept a callback
function getResponse(callback)
{
    params = "apikey=test"
    request = new ajaxRequest()
    request.open("POST", "processapi.php", true)
    request.setRequestHeader("Content-type",
        "application/x-www-form-urlencoded")
    request.setRequestHeader("Content-length", params.length)
    request.setRequestHeader("Connection", "close")
    request.onreadystatechange = function()
    {
        if (this.readyState == 4)
        {
            if (this.status == 200)
            {
                if (this.responseXML != null)
                {
                    // ***CHANGE*** Trigger the callback with the response
                    callback(this.responseXML);
                }
                else alert("Ajax error: No data received")
            }
            else alert( "Ajax error: " + this.statusText)
        }
    }

    request.send(params)
}

然后,而不是这个:

out = ""
xmlDoc = getResponse()
users = xmlDoc.getElementsByTagName('username')
for (count = 0; count < users.length ; count++)
{
    out += "Username: " + users[count].childNodes[0].nodeValue + '<br />'
}
document.getElementById('output').innerHTML = out

你会这样做:

getResponse(function(xmlDoc) {
    out = ""
    users = xmlDoc.getElementsByTagName('username')
    for (count = 0; count < users.length ; count++)
    {
        out += "Username: " + users[count].childNodes[0].nodeValue + '<br />'
    }
    document.getElementById('output').innerHTML = out
});

请注意我是如何将所有依赖于响应的代码移动到一个函数中,然后将该函数传递到getResponse. getResponse当它有数据时调用它。请求/响应、事件驱动、异步,无论你想怎么称呼它,这是尽早加入的关键。:-)


旁注:您的代码正在成为The Horror of Implicit Globals 的牺牲品。强烈建议声明所有变量,并将所有代码包装在一个作用域函数中以避免创建全局符号(浏览器上的全局命名空间已经人满为患)。

于 2012-12-01T11:12:55.270 回答
0

getResponse正在执行异步 XHR 请求,因此函数将在结果准备好之前返回。您需要改用回调方法,

更改函数声明以接受回调参数:

function getResponse(callback) {

然后在函数体中修改这部分

if (this.responseXML != null)
{
    return this.responseXML; // WRONG
}

以便它在接收到 XML 时调用回调

if (this.responseXML != null)
{
    callback(this.responseXML); // GOOD
}

然后你会这样打电话getResponse

getResponse(function(xmlDoc){
   // do something with xmlDoc
})
于 2012-12-01T11:15:47.293 回答