Dynamic script addition should be ordered? - javascript

I'm adding some <script> tags dynamically to the head element after page load. I understand the scripts are loaded asynchronously, but can I expect them to be parsed in the order they are added?
I'm seeing the expected behaviour in Firefox, but not in Safari or Chrome. Looking at the document in Chrome developer tools and Firebug, both show the following -
<html>
<head>
...
<script type="text/javascript" src="A.js"></script>
<script type="text/javascript" src="B.js"></script>
</head>
...
</html>
However looking at the resource loading view, chrome seems to parse whichever is returned first from the server, while firebug always loads them in the order the script tags were added, even when B is returned first from the server.
Should I expect Chrome/Safari to parse the files in the specified order? Using Chrome 5.0.375.29 beta on OS X 10.6.3
EDIT (10/5/10): When I say parse, I mean execute - can see many benefits of aggressive parsing - thx rikh
EDIT (11/5/10): Ok so I put together a test along the lines of that by juandopazo below. However I have added a combination of things, including
Adding the script element to the head directly with javascript. (Tests A -> D)
Adding the script element to the head using jquery's append() method. (Tests E -> H)
'Loading' the script with jquery's getScript() method. (Tests I -> L)
I also tried all combination of the 'async' and 'defer' attributes on the script tags.
You can access the test here - http://dyn-script-load.appspot.com/, and view source to see how it works. The loaded scripts simply call the update() function.
The first thing to note, is that only the 1st and 3rd methods above operate in parallel - the 2nd executes requests sequentially. You can see a graph of this here -
Image 1 - Graph of Request Lifecycle
Request lifecycle Graph http://dyn-script-load.appspot.com/images/dynScriptGraph.png
It's also interesting that the jquery append() approach also blocks getScript() calls - you can see that none of them execute until all of the append() calls are complete, and then they all run in parallel. Final note on this is that the jQuery append() method apparently removes the script tags from the document head once they have executed. Only the first method leaves the script tags in the document.
Chrome Results
The results are that Chrome always executes the first script to return, regardless of the test. This means all the test 'fail', except the jQuery append() method.
Image 2 - Chrome 5.0.375.29 beta Results
Chrome Results http://dyn-script-load.appspot.com/images/chromeDynScript.png
Firefox Results
On firefox, however, it appears that if the first method is used, and async is false (i.e. not set), then the scripts will reliably execute in order.
Image 3 - FF 3.6.3 Results
FF Results http://dyn-script-load.appspot.com/images/ffDynScript.png
Note that Safari seems to give varied results in the same manner as Chrome, which makes sense.
Also, I only have a 500ms delay on the slow script, just to keep the start->finish time down. You may have to refresh a couple of times to see Chrome and Safari fail on everything.
It seems to me that without a method for doing this, we are not taking advantage of the ability to retrieve data in parallel, and there is no reason why we shouldn't (as firefox shows).

Sorry for answering my own question, but its been a while and we did come up with a solution. What we came up with was to load the javascript concurrently as text contained in a json object, and then used eval() once they were all loaded to execute them in the correct order. Concurrent load plus ordered execution. Depending on your use case you may not need the json. Roughly, here is some code that shows what we did -
// 'requests' is an array of url's to javascript resources
var loadCounter = requests.length;
var results = {};
for(var i = 0; i < requests.length; i++) {
$.getJSON(requests[i], function(result) {
results[result.id] = result;
...
if(--loadCounter == 0) finish();
});
}
function finish() {
// This is not ordered - modify the algorithm to reflect the order you want
for(var resultId in results) eval(results[resultId].jsString);
}

As I understand it, they are meant to be executed in the order they appear in the document. Some browser might be able to perform some parsing out of order, but they would still have to be executed in the correct order.

No, you cannot expect that all browsers will defer execution of both scripts until both are loaded (**especially when you are adding them dynamically).
If you want to execute code in B.js only after A.js is loaded then your best bet is to add an onload callback to A.js that sets a variable and another one to B.js that checks to see if that variable has been set, then it executes the necessary function in B.js if it has (and if A.js has not loaded, it starts a timer that periodically checks until it has loaded).

The download order and the execution order is not the same thing. In your page, even if B.js is downloaded first, the browser's engine will wait for A.js to continue processing the page.
The scripts are definitely processed, not only in the order they appeared in the document, but also at the place they appeared.
Imagine if it wouldn't be like that, there would be many errors if your little script that uses jQuery is downloaded and processed before the jQuery library.
Also, when you do a "document.write" in a js file, it appears where the script has been declared. You can't access DOM objects that are appearing after the script declaration neither.
This is why there are recommendations to put scripts at the very bottom of the page, to prevent their execution too soon and decrease the "perceived load time" of the page, because the browser's rendering engine is stopped as soon as a script is processed.
Mike
EDIT: if they are added dynamically with javascript, I think they are processed in the order they were added in time.

You could load b.js from a.js to be 100% sure ... although I'd like the definitive answer to this question myself, especially with sync ajax loading of scripts.

I was investigating this while working on a little library that loads modules dynamically like YUI 3. I created a little test here that loads two scripts that just insert content into divs. One is a common JS file and the other is a PHP file that waits 3 seconds to execute.
http://www.juandopazo.com.ar/tests/asyn-script-test.html
As you can see, scripts are executed when they finish loading, and not in the order in which you append them to the DOM, in every browser.

Related

When does the browser's event loop start?

I'm using a framework which features auto-connecting to server on page load. I can disable it by passing options arguments, but the line that confuses me is this:
You can prevent this initial socket from connecting automatically by disabling io.sails.autoConnect before the first cycle of the event loop elapses.
My questions are:
When does the first cycle of the event loop elapses?
Is this behaviour the same across ALL modern (IE9+) browsers?
I have a bunch of scripts (in <body>) loading between the lib and my entry file. Does this affect when the first cycle elapses? EDIT: Yes, it does.
How can I ensure my code runs before the first cycle elapses?
Is this kind of implementation of auto-connect considered good practice?
The documentation for the source file is a little more explicit; it says "This can be disabled or configured by setting io.socket.options within the first cycle of the event loop."
Basically what's happening is that there exists within the library a setTimeout(fn, 0) call, which is idiomatic for starting a parallel process. However, in the JS standards it's explicitly stated that JS is single-threaded: in other words, even though setTimeout and setInterval are asynchronous they are not actually parallel in the sense that any of their code will be executing simultaneously with any other code. So they wait until the current function is over before they execute. This queueing mechanism is known as the JavaScript event loop.
I believe that what you are asked to do by the script author is to modify the source to include the relevant change, perhaps at the bottom of the file for your convenience.
It is also likely that a similar effect will be achieved by putting a <script> tag underneath the one that loads the given JS. This has not been explicitly standardized by HTML 4, but may be implicitly standardized in the new HTML 5 spec (it's a complicated interaction between different parts of the specs).
In terms of HTML5, it looks like the current specs say that there is a afterscriptexecute event and a load event which occur immediately after any remote script is loaded (or, if it's an inline script, the load event is scheduled as a task -- I am not sure when those occur). So you might be able to guarantee it without modifying the script by instead doing:
<script>
function do_not_autoload() { /* ... */ }
</script>
<script onload="do_not_autoload()" src="./path/to/sails.io.js"></script>
but I'm not sure what the compatibility table for script#onload is going to look like.
I made you a jsfiddle which can be used to grab a 'fingerprint' for different browsers to get an idea of what evaluation orders are out there in the wild. The * is the document.body.onload event. On my system it produces:
Firefox 32.0.3 : cafdbe*
Chrome 37.0.2062 : cafd*be
IE 11.0.9600 : cafd*be
In other words,

Firefox Extension: Access the DOM Before It's Loaded

I'm trying to create a Firefox extension that fires my Javascript code before any of the current page's Javascript is fired. My Javascript code will basically control whether or not the page's Javascript code can be executed or denied.
I first started out by trying to follow this answer, but I couldn't really figure out how to get it to work and realized I was relying on onDOMContentLoaded, which loads after the Javascript has already executed.
I then turned my attention toward XPCOM, but once again didn't really understand what the Firefox tutorials were telling me.
I've recently been trying to make an extension through Firebug, but I seem to hit the same problem... only having access to the Javascript after it's been parsed/executed. Here's the resulting code that I wrote. I think if I could access the file's objects in the onExamineResponse event, my problem could be solved, but I don't know how to do that... I'm talking about this code:
BeepbopListener.prototype = {
onRequest: function(context, file) {
...
},
onExamineResponse: function(context, file) {
FBTrace.sysout("onexamineresponse " + file); // this returns something like
// '[xpconnect wrapped (nsISupports, nsIHttpChannel, nsIRequest, nsIUploadChannel, nsITraceableChannel, nsIHttpChannelInternal)]'
// but I don't know how to access those elements...
var pattern = /\.js$/;
if (pattern.test(file.href) && FBTrace.DBG_BEEPBOP) {
FBTrace.sysout("ONEXAMINE DOESN'T EVEN GET IN THIS IF SO YOU WON'T SEE THIS");
}
},
...
};
So my question is... is there a tutorial out there that shows me how I can get access to all Javascript code on a page before it's executed? Also, if anyone has any helpful insight, I'd love to hear it. Oh, and if y'all need more code from me, just let me know, and I'll post it.
You can access a new document before any JavaScript code runs by listening to the content-document-global-created observer notification. However, the document will be empty at this point and JavaScript code will run as soon as the parser adds a <script> tag - you cannot really prevent it. Here are the options to control script execution that I am aware of.
1) Disable all JavaScript for a window using nsIDocShell.allowJavascript:
wnd.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
.getInterface(Components.interfaces.nsIWebNavigation)
.QueryInterface(Components.interfaces.nsIDocShell)
.allowJavascript = false;
This is an all or nothing approach. Note that JavaScript stays disabled even when a new document loads into the same frame.
2) Implement the nsIContentPolicy interface in an XPCOM component and register it in the content-policy category (via nsICategoryManager). Your shouldLoad() function will be able to block scripts selectively - but it will only called for external scripts (meaning <script src="...">), not for inline scripts on the page.
3) Use JavaScript debugger service to intercept script execution. You could use jsdIDebuggerService.interruptHook to step through JavaScript execution and abort the script whenever you like. But that would slow down JavaScript execution very significantly of course. At the very least you should use jsdIDebuggerService.addFilter() to restrict it to a particular document, otherwise you will slow down the entire browser (including browser UI).
I'm trying to create a Firefox extension that fires my Javascript code before any of the current page's Javascript is fired. My Javascript code will basically control whether or not the page's Javascript code can be executed or denied.
Start by completely preventing the document from getting parsed altogether then on the side, fetch the same document, do any processing on this document and then inject the resulting document in the page. Here is how I currently do just that https://stackoverflow.com/a/36097573/6085033

Identify which Javascript executed a call using Firebug or Google Chome

How to know which Javascript (.js) executed a GET or Ajax call using Firebug or Google Chrome Plugin?
For example, a request for a image or html file executed by a Javascript, how to know which Javascript on the page executed that
Update:
I have to (shamefully) admit that the original below was wrong. Due to the nature of the js execution flow this works at the first execution time:
console.log($("script").last().attr("class")); //or whatever
That, however, is not good enough, as illustrated by a call on a timeout. We need to keep the reference to the script element, this can be achieved by wrapping script contents into a closure and creating a variable to store the jQuery reference:
(function(){
var $scriptElement = $("script").last();
console.log($scriptElement.attr("class")); //or whatever
})();
Now, I have to disclaim that with the markup as above it is unlikely to be practical unless you come up with a better way to store the script element reference... this pretty much became a rather bad example of what could be done but really shouldn't :(
Unless you have to get the reference in-code, you would be much better off looking at the console output, it actually tells you where the output originated from, with the line# and everything:
Original:
Not sure how applicable this would be to external js (script tag with a src), but for inline scripts you could do something like this w/jQuery:
$(this).closest("script");//gets you reference to the script element
I'm assuming it would just a matter of getting its src attribute! Let us know if it works.
console.log($(this).closest("script").attr("src"));
In chrome you can break on any xhr request. This will only set breakpoints for AJAX calls though.

Canceling dynamic script by removing script tag doesn't work in FF

I'm adding dynamic script by creating a script tag, setting its source and then adding the tag to the DOM. It works as expected, the script is getting downloaded and executes. However sometimes I would like to cancel script execution before it was downloaded. So I do it by removing the script tag from the DOM.
In IE9, Chrome and Safari it works as expected - after the script tag is removed from the DOM it doesn't execute.
However it doesn't work in Firefox - script executes even if I remove it from the DOM or change it its src to "" or anything else I tried, I cannot stop the execution of a script after it was added to the DOM. Any suggestions?
Thanks!
How about some sort of callback arrangement? Rather than have the dynamically added script simply execute itself when it loads, have it call a function within your main script which will decide whether to go ahead. You could have the main script's function simply return true or false (execute / don't execute), or it could accept a callback function as a parameter so that it can decide exactly when to start the dynamic script - that way if you had several dynamic scripts the main script could wait until they're all loaded and then execute them in a specific order.
In your main script JS:
function dynamicScriptLoaded(scriptId,callback) {
if (scriptId === something && someOtherCondition())
callback();
// or store the callback for later, put it on a timeout, do something
// to sequence it with other callbacks from other dynamic scripts,
// whatever...
}
In your dynamically added script:
function start() {
doMyThing();
doMyOtherThing();
}
if (window.dynamicScriptLoaded)
dynamicScriptLoaded("myIdOrName",start);
else
start();
The dynamic script checks to see if there is a dynamicScriptLoaded() function defined, expecting it to be in the main script (feel free to upgrade this to a more robust test, i.e., checking that dynamicScriptLoaded actually is a function). If it is defined it calls it, passing a callback function. If it isn't defined it assumes it is OK to go ahead and execute itself - or you can put whatever fallback functionality there that you like.
UPDATE: I changed the if test above since if(dynamicScriptLoaded) would give an error if the function didn't exist, whereas if(window.dynamicScriptLoaded) will work. Assuming the function is global - obviously this could be changed if using a namespacing scheme.
In the year since I originally posted this answer I've become aware that the yepnope.js loader allows you to load a script without executing it, so it should be able to handle the situation blankSlate mentioned in the comment below. yepnope.js is only 1.7kb.

What's with the random Javascript errors?

I'm developing a site in javascript and jquery. Sometimes when I refresh I just get different random errors in firebug. What's the deal?
edit: I'm getting errors like a variable isn't defined, when clearly it is and working, and when i refresh again, the error is gone..
using Firefox V3.5.5 Firebug V.1.5.3 and I'm primarily working with jQuery 1.4.2
OK. While it's more or less impossible to give a reasonable solution to such a general question, I'll just add my 2 cents' worth:
One possible source of "undefined variable" errors comes from including several scripts, which may or may not always load and execute in the same order. If you define a variable in one script (let's call that script declare.js) and use it in another (let's say use.js), and use.js is executed before declare.js, then you will get such an error. If the scripts execute the other way around, everything will appear fine.
If you're interested in this very topic, have a look at e.g. Steve Souders' book Even faster web sites, published by O'Reilly. More specifically, look at the chapter about non-blocking script loading.
Most common cause is that you're trying to execute Javascript before the DOM is loaded and thus before all HTML elements are available in the DOM tree, which in turn may cause that simple calls like document.getElementById(id) and jQuery's $(selector) may return undefined elements. That it sometimes works is pure coincidence and a matter of timing.
You need to ensure that any Javascript/jQuery code which is supposed to be executed during page load and relies on the availability of the elements in the DOM tree, also really get executed after the DOM is loaded. In plain vanilla JS you can do so:
window.onload = function() {
document.getElementById(someId);
}
and in jQuery:
$(document).ready(function() {
$(someSelector);
});

Categories