I am sure I don't fully understand this problem, but it seems that we are seeing strange behavior on IE9 on my project, somehow related to out-of-order execution of JavaScript that has been injected via calls to document.write, e.g.:
document.write('<scr'+'ipt type="text/javascript" src="'+file1+'"></src'+'ipt>');
document.write('<scr'+'ipt type="text/javascript" src="'+file2+'"></src'+'ipt>');
document.write('<scr'+'ipt type="text/javascript" src="'+file3+'"></src'+'ipt>');
My limited Google research suggests that IE9 will execute scripts injected in this manner in a different order from other browsers (notably, Firefox and Chrome). Is there a better way to achieve what we're going for here, which will ensure the same execution order by all browsers?
I take that back: we don't really care about all browsers, just Chrome and IE9.
Use a script loader (like the one I wrote: LABjs), which will normalize all the different quirks of loading across the various browsers. And bonus: it doesn't use that god-awful document.write(). LABjs will let you load all your scripts asynchronously (in parallel), but make sure they execute in the proper order. Sounds like basically exactly what you want.
I guess you could chain the onload event of one to start the load of another:
var newJS= document.createElement('script');
newJS.onload=function() {alert("done")} //or call next load function
newJS.src="..."
document.body.appendChild(newJS)
So the advantage of writing script tags this way is that they are loaded asynchronously. I don't know about browser nuances about exactly how this is done but I would have thought they would be executed when they're downloaded, in no specific order. Similar to the behaviour of the HTML5 async attribute.
There's another HTML5 attribute defer which instead makes scripts execute in order, but in a non blocking way. You could try adding that into your generated <script> tags. IE9 partially honours it.
I have made a little script, exactly for this purpose:
https://github.com/mudroljub/js-async-loader
In short, it loads all your scripts asynchronously, and then executes them consequently. It looks something like this:
for (var lib in libs) {
loadAsync(lib);
}
And you don't need document.write();
Related
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,
Ilya Grigorik recommends the use of the <script async> tag where possible.
Is there a clean, preferred way to load an Angular.js app using the tag, without using tools like require.js or the $script.js tool recommended by the angular-seed?
The obvious issue is execution order. e.g. preventing:
Uncaught ReferenceError: angular is not defined
https://www.igvita.com/2014/05/20/script-injected-async-scripts-considered-harmful/
If there's something useful you can show without angular (e.g., a pre-generated content using a headless browser) AND nothing but your script depends on it1, then there can be a solution:
load angular.js using async
encapsulate you code in a function body
add a loop checking if angular is defined
and sleeping for a few milliseconds if not
otherwise, executing your code and breaking out of the loop
This sort of busy waiting is ugly, but I can't see how to get called back when the loading finishes. It may work or not, I haven't tried it yet.
It's quite possible that I'm doing nothing but a primitive version of what the frameworks do.
If you don't need it to work in all browsers, then there's the defer tag. To me, defer looks like async done right.
1 So I guess, users of angular-route.js or alike are out of luck. This is just a guess as I've never tried to load the two out of order.
According to this answer you might want to try to use defer instead of async, which leads to the browser to respect the loading order. E.g.:
<script src="scripts/vendor-including-angular.js" defer></script>
<script src="scripts/your-app.js" defer></script>
You may want to merge all dependent JS files into one before using async.
Use Grunt, Gulp, Broccoli or another task runner for this.
If the script is modular and does not rely on any scripts then use async.
Source: Frontend performance tips for web developers.
DON'T USE async attribute with Angular.js script tag.
It seems that using async attribute is harmful. Without it, expressions are evaluated on DOMContentLoaded event, but with the async attribute, there are evaluated on window load event, that is much later. Tested for Angular.js 1.0 and 1.4 in latest version of Firefox, Chrome and IE11.
I have some serious problem with getting asynchronously some js libs and executing them in $(window).load in IE
all works in other browsers of course
so the problem is, that I'm doing something like
<script type="text/javascript">
var scr1 = document.createElement('script');
scr1.type = 'text/javascript';
scr1.src = 'some_lib.js';
$('BODY').prepend(scr1);
</script>
Just before </body> and use $(window).load method in html above it to operate on some plugins in some_lib.js, but it all happens to fast in IE, probable because of that asynchronous lib including, and I get an error, that method is not available for the element.
Is there any chance of maybe modyfying $(window).load method so I still could use it in the same way for every browser ?
Any code that you have in the window.load() call must be placed in a function (called onLoad in this example).
Every time you have a script that you dynamically load, increment a counter. Also include something to decrement that counter...
src1.onload = function() { counter--; onLoad(); }
Then in 'onLoad' have the first line...
if (counter > 0) return;
That means that onLoad will fire at window.load and after every script is loaded, but will only execute when it's all loaded.
It's scrappy, but it will solve your problem.
You haven't really described the reason you need to load these libraries asynchronously. Third party libraries often have "on script load" functionality that you can define before the script is loaded. If you need to load multiple libraries before you can execute your code, you may have to either 1. fire up some code every time a library is loaded to test to see if all libraries required are loaded and then fire off you code 2. for every library, create a jQuery promise/deferred to get resolved when that library is loaded and use $.when(promises).done(function/code) to test and run the code whenever a particular set is loaded, or 3. rewrite to use RequireJS. If these libraries are YOUR code, well, you may have to add start up code to your libraries anyway; It might be a good time to learn RequireJS.
I wish I could recommend further, but learning the basics behind RequireJS has always been on my todo list, but it hasn't been done; I just know of people here successfully using it. If that seems like too much trouble, I'd consider some variant of option 2. If you don't know what jQuery would be used eh... you may be stuck with option 1 or 3.
Edit:
Of course, that's not to say that jQuery has got the only promise library, I just often recommend using promises in some form for these kind of things..
Archer's technique looks interesting, I just don't know how reliable it is (it might be quite reliable, I just would like to see proof/documentation). You could combine that with option 2 also, quite well, if you want to short-cut execution for some things while leaving others to be dealt asynchronously and if those script onload methods really work as expected.
I am trying to figure out a problem with some code I have inherited.
I have an HTML page with
<script type="text/javascript" src="file1.js" defer="defer"></script>
<script type="text/javascript" src="file2.js" defer="defer"></script>
</body>
</html>
file1.js has
FOO = {
init : function () {
var bar = BAR;
}
}
$(document).ready(FOO.init);
file2.js has
var BAR = {
}
Because of the defer attribute on the elements, is it safe to assume that when the .ready() calls FOO.init() that BAR may still be undefined at that point b/c the code in file2.js hasn't executed yet because of the deferred execution?
This would match a bug I am trying to track down (only occurs sporadically in IE), but I really want to understand why this is happening before I work on a solution. I have no idea why the original developer used defer, other than a cryptic commend about "he had to" do it this way.
Defer should cause the script to be added to a queue that is processed after the page is completely loaded. According to the spec deferred scripts should be added to the queue in the order they came onto the page.
However different browsers have done slightly different things with the order. IE seems to run defer scripts in the order they finished loading rather than the order they occurred on the page. So you seeing the error sporadically because sometimes it's loading them in the right order and sometimes not.
See this post on hacks.mozilla.com for a more exhaustive explanation and examples of how different browsers handle the ordering of the defer queue.
Deffering in javascript gives preference to the browser of when to interpret the script, in some optimal conditions like with chrome the script is downloaded while the page is being loaded then parsed and interpreted. If you use defer like the above you can never be certain which script is loaded first or when the interpretation is complete.
BAR could be undefined on one page load and be defined on the reload (cached) or the second script was loaded first.
To test this try make a change to one of the scripts to force a new download and interpretation and see what race conditions exist.
I ask because I'm running an application in which I load an external script file in the HEAD section of the page, and then attempt to call a function from it in the onLoad section of the BODY tag.
external.js
function someFunction()
{
alert("Some message");
}
myPage.html
<html>
<head>
<script type="text/javascript" language="javascript" src="external.js"></script>
</head>
<body onLoad="someFunction();">
</body>
</html>
Using the developer tools in IE8, I get an exception thrown at the onLoad statement because, apparently, the external javascript file hasn't been loaded yet.
I haven't had this problem come up in IE7 before, thus my question.
Did they change the load order between IE7 and IE8? If so, is there a better way to do this? (the real function references many other functions and constants, which look much better in an external file)
Thanks,
B.J.
Well, I feel pretty stupid actually.
Turns out the problem wasn't with the load order. The problem was that the external javascript file had a syntax error in one of its functions, and apparently when the exception was thrown it completely invalidated the whole file, thus making the rest of the functions unavailable to the main page.
I'm not sure if this behavior is different in IE8 compared to IE7, but anyway, that was the real problem.
Thanks for your reply.
B.J.
I doubt very much that his has changed it would break a considerable number of websites.
Try this (without using the developer tools):-
<body onload="alert(somefunction)">
this shouldn't break and will tell you whether at the point onload executes whether the identifier somefunction can be seen.
Assuming that what you think is happening is what is happening, you should try to attach the body.onLoad later on.
To simplify things, you can do it with Prototype (including prototype, of course) with
Event.observe(window, 'load', function() { myFunction.init() });
or JQuery (including JQuery) with
$(document).ready(function(){
// Your code here...
});
I think there is a pure Javascript way to do this, but the problem is that the body element won't exist yet, so it's rough...
That said, I have had no problems running body onload in Javascript with IE8, and putting it right into the body tag, using external files. I'm going to test that right now out of curiosity, and I'll report back.
Edit: There's no problem doing the onload from an external file. However, while we're at it, you might want to get to know JQuery, Prototype or Scriptaculous :)