I am going through some very basic AJAX programming in “Learning PHP, MySQL & JavaScript -O’Reilly”. There are a couple very basic programs with Java getting some text or a page with AJAX. I am using the W3Schools.com site to try to learn more about getting XML formatted docs with AJAX. I have two questions related to what I am learning.
When I used the code from http://www.w3schools.com/dom/dom_loadxmldoc.asp I can return an XML document with the snippet below:
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");
But async has to be set to false for this to work. If I set async to true, it returns null. So my first question is why must async be false for this to return the XML document?
My second question, when I try to combine some of the code from the book and some from the website, snippet follows:
<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>
I get an error that “TypeError: xmlDoc is undefined”. In function getResponse() with async set to true or false it doesn’t work, I get the xmlDoc is undefined error. The xmlDoc is not defined elsewhere in the first example (from w3schools.com) before being set to the return of loadXMLDoc() and that works just fine. the ajaxRequest() is out of the book, and most everything in getResponse() is out of the book, but I put it in the getResponse() function. processapi.php just echos an XML document when called from this script.
Why is async not working in the first example, and why is xmlDoc undefined in the second? Any help is greatly appreciated.
The answer to both of your questions lies in the nature of asynchronousness. With
asyncbeingtrue, this returnsnull:…because the HTTP request hasn’t completed by the time the
returnstatement occurs, and soxhttp.responseXMLstill has its default value (null). The HTTP request completes later, out of the flow of that code (asynchronously — literally, not synchronous).Your
getResponsefunction never returns a value at all. The anonymous callback function you’ve assigned toonreadystatechangereturns a value, but that doesn’t in any way affect the return value ofgetResponse.One of the key aspects of client-side web programming is embracing the asynchronous, event-driven nature of the environment. There are virtually no use cases for setting
asynctofalseon XHR requests. Instead, get used to using callbacks for things. For instance, yourgetResponsefunction could look like this:Then, instead of this:
you’d do this:
Note how I moved all of the code that relied on the response into a function, and then passed that function into
getResponse.getResponsecalls it when it has the data. Request/response, event-driven, asynchronous, whatever you want to call it, this is a key thing to take onboard early. 🙂Side note: Your code is falling prey to The Horror of Implicit Globals. Strongly recommend declaring all of your variables, and wrapping all of your code in a scoping function to avoid creating global symbols (the global namespace on browsers is already overcrowded).