Xavier Xavier - 1 year ago 49
Javascript Question

Parsing XML before Javascript function executes

I have an external XML file that I want to parse using Javascript but, before another Javascript function executes. I want to store values from the XML file in a global object. The problem is that I am parsing my XML asynchronously as synchronous parsing is deprecated. The Javascript function executes before the XML is parsed, so I get an error because the global variables are

undefined
.

I tried calling the function that parses the XML file in one Javascript file, then calling the function in another file using the
defer
attribute. But this does work because the parser still executes after all scripts are executed. I am stumped! I have also tried using the
setTimeout()
method on the Javascript function but, that also produces undefined values in the end.

I have read several questions on stack overflow that have to do with the order in which scripts are executed. I will say that this problem has taught me a lot about execution order.

NOTE: There is much more to my scripts than shown here so I just added the bare essentials to illustrate how I am parsing my XML file and that the values get stored as undefined.

UPDATE: I have written the parser this way so that I can use in a neutral way. The parser is used many times and each time different events happen depending on the use. That is why I am not adding specific functions to the
onreadystatechange
function.

I am using
XML DOM
and
XMLHttpRequest
to parse my XML file.

// Parser
function load_prs( data_func , loc , tag )
{
var parsreq = new XMLHttpRequest() ;

parsreq.onreadystatechange = function() {
if ( (parsreq.readyState === 4) && (parsreq.status === 200) )
{
var xml = parsreq.responseXML ;
var listing = xml.getElementsByTagName(tag) ;
var list = listing[0].children ;
data_func(list) ;
}
} ;

parsreq.open("GET" , loc + ".xml" , true) ;
parsreq.send() ;
}


Call the function to parse:

load_prs(load_playlist , "nd/playlist" , "playlist") ;


The function
load_playlist()
assigns the XML values to a global object:

var playlist_obj = { band: "" , album: "" , title: "" } ;
var playlist = [] ;
function get_val( tar , tag ) { return tar.getElementsByTagName(tag)[0].childNodes[0].nodeValue ; }

function load_playlist( list )
{
var px ;
var plen = list.length ;
for ( px = 0 ; px < plen ; px++ )
{
playlist_obj.band = get_val(list[px] , "band") ;
playlist_obj.album = get_val(list[px] , "album") ;
playlist_obj.title = get_val(list[px] , "title") ;
playlist.push(playlist_obj) ;
}
}


My XML file:

<?xml version="1.0" encoding="UTF-8"?>
<playlist>
<track>
<band>Desecresy</band>
<album>Stoic Death</album>
<title>Funeral Odyssey</title>
</track>
<track>
<band>Catacombs</band>
<album>In the Depths of R'lyeh</album>
<title>Where No Light Hath Shone...</title>
</track>
</playlist>


When I try to call another function that accesses the global object in the same script, I get undefined values. The execution order looks like this:

load_prs(load_playlist , "nd/playlist" , "playlist") ;
function load_band() { var t = playlist[0].band ; }
load_band() ;

Answer Source

Well, I wanted to give Martin Honnen the answer but, he seems to be unavailable. So I am going to answer myself. But Martin Honnen did answer my question (look in comments). While Promises would have been a great solution, I felt that the answer could have been simpler without the need to add more code and make the situation more complex than it already is.

So the answer was actually quite simple. By adding the load_band() function directly into the load_playlist() function, my variables got their values in the order I wanted. I just had to make sure that all functions called were called from the functions that were passed to the parsing function. I had to rewrite my code extensively but, it was worth it. Now my load_prs() function can be used many times, as a neutral function. It gets the values and can then do different things with those values, depending on the intent, with the specific function that is passed to it. Now the example looks like this:

function load_playlist( list )
{
var px ;
var plen = list.length ;
for ( px = 0 ; px < plen ; px++ )
   {
   playlist_obj.band = get_val(list[px] , "band") ;
   playlist_obj.album = get_val(list[px] , "album") ;
   playlist_obj.title = get_val(list[px] , "title") ;
   playlist.push(playlist_obj) ;
   }
load_band() ;
}

function load_band() { var t = playlist[0].band ; }
load_prs(load_playlist , "nd/playlist" , "playlist") ;

NOTE: When I got this example to work, I discovered that my playlist array had objects that were all identical. This was because I was using a global object with name value pairs and trying to give it different values at different times.

var playlist_obj = { band: "" , album: "" , title: "" }

I was able to solve this by using the object constructor.

// Object constructor
function pobj( b , a , t )
{
this.band = b ;
this.album = a ;
this.title = t ;
}

// Create local variables
var band = get_val(list[px] , "band") ;
var album = get_val(list[px] , "album") ;
var title = get_val(list[px] , "title") ;

// The new keyword creates a new object
playlist_obj = new pobj(band , album , title) ;

// Store the new object in the global array
playlist.push(playlist_obj) ;
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download