views:

36

answers:

1

Problem description:

I'm working on a project to take an existing JavaScript script and modify it to support being loaded asynchronously. If you don't know about asynchronous or lazy loading scripts but want to learn more read this.

One of the first things our main.js script does is call our web-server (3rd party to our clients) to get some dynamic data. Which is the problem because that data is return via a dynamically added JS file (data.js), which contains variables that are needed in other parts of main.js. The problem is similar to the issue described in chapter 5 of the book Even Faster web sites. Which deals with coupling inline scripts with async scripts. Anyway... The problem being that main.js doesn't know when data.js has finished loading...

Currently main.loadDataJs() uses the "Document.write Script Tag" technique to dynamically load our JS file, but this doesn't work for async scripts (for anyone who hasn't tried it: it results in a blank page after loading). So, we have to use the "Script Dom Element" technique of loading data.js dynamically. The problem here is figuring out when the browser has downloaded and executed the added script node. So I'm looking for a method of coupling data.js with the async script main.js.

My priority is to build something that is reliable on both PC browsers and mobile browsers. It has to have a very small and light weight footprint, because the amount of data being returned by data.js is pretty small (about 5-10 key/value pairs). It has to support 3rd party communication.



Solutions I'm investigating:

  • From my own research, I'm leaning towards modifying the server code that serves up data.js to support JSONP. That is: when we set the src attribute of the script object the call would have a callback parameter src="data.js?callback=fooCallback" that causes the data to be wrapped in a function call, like so fooCallback( { geoC : "US" } );. From my experimenting this seems to be the most reliable solution, since it isn't browser dependent. Deploying server-side code means greatly increasing the scope of the project, but C'est la vie.

I also experimented with:

  • Using a polling Timer. Which checks for the data to exist and if so call the "callback" function, else calls setTimeout again. My problem with this solution is that it isn't event driven so it doesn't respond as quickly as I like, and it causes the browser's busy indicator to activate. So if data.js takes a long time to download or fails, it could "look" busy for a long time.

  • Using the script block's onload and onreadystatechange handlers. This would have been a perfect solution except for two issues I hit.
    A) On a fast machine using IE8, I was able to get the onreadstatechange to fire the s.readyState === "loaded" event before it had executed data.js, I assume it was downloaded but still being executed :-/. I was able to fix this by adding a setTimeout(fooCallBack, 1); call inside the onreadystatechange handler. But I'm not a 100% happy with that solution.
    B) I have read (but haven't tested yet) that this technique doesn't work on IE6 minus. Any comments?

I looked into other non-dynamic-JavaScript solutions but they all seemed heavier handed than I was looking for. Like the iframe or flash techniques used by Facebook and others.



My Questions:

From anyone who has coupled async scripts together, I'm curious what solutions you choose and how reliable you have found them?

Is there some alternative solution I haven't talked about?

Any general comments on anything I've talked about?



Definitions:

The definitions of the "techniques" are borrowed from Steve Souders's book Even Faster web sites. A great book BTW!

script dom element – a method of adding a dynamic script by creating a script element and setting its src property to the script’s URL.

document.write script tag – a method of adding a dynamic script by writing the HTML into the page using document.write.


+1  A: 

If you need an event called when a script has loaded, try the following: in main.js create a function main.loadDataJS.onload = function(evt) { } and have each loaded script (assuming you're the creator of those as well) could call that function (passing evt) at its end.

Of course, if you don't have access to the script (for example, say you're loading Scott Schiller's soundManager2.js), well then odds are that library/script has its own onload event which you can use to call your own loader. (Following the above example code, you would simply place soundManager = {}; soundManager.onload = function(evt) { main.loadDataJS.onload(evt); } in main.js.)

As for loading the script, any of the methods you listed should work just fine. I'd personally recommend jQuery's getScript method.

Adam
I would basically consider the JSONP method I'm considering a subset of the callback methods many frameworks are now starting to use. Since our "data.js" file is just key/value pairs JSONP is all we need. Looking at the Jquery code I can see it use the onLoad/OnReadyStateChange method when the domain is "remote" aka (3rd party) and XHR script injection if 1st party. Thats a pretty good lead, I wouldn't want to include Jquery into our own libary since it would nearly double it's size but their version of the onLoad/OnReadyStateChange is a good lead for how to use that method.Thank you !
eSniff