I thought I understood how the setTimeout method worked, but this is confusing me.
test.html (I'm purposefully loading the test.js file before the jQuery file for demonstration. Let's say the jQuery file is hosted locally).
<body>
// ...code
<div id="area"></div>
// ...code
<script src="test.js"></script>
<script src="jquery.js"></script>
</body>
test.js
$('#area').text('hello');
I understand in this case "hello" won't get printed on the browser because jQuery is being loaded after the test.js file. Switching the order of these files solves the problem. But if I leave the order alone, and alter the test.js file, a setTimeout makes it work:
function wait() {
if(window.jQuery) {
$('#area').text("hello");
}
else
{
setTimeout(wait, 10);
}
}
wait();
In this case the "hello" text gets printed on the browser. But I'm sort of scratching my head because somehow the jQuery file does get loaded. But how? Why doesn't the test.js file get caught in an infinite loop forever checking to see if jQuery has loaded? I'd be grateful for some insight on the mechanics of what's going on.
There would be an infinite loop if jQuery never loaded. But in the normal case:
The first time through, jQuery isn't loaded, so we setTimeout()
1a. Other things happen in the meantime, including loading of resources like jQuery
10ms later, we check again.
Is jQuery loaded now? If not, set a timeout and go back to step two
After some number of retries, jQuery does load, and we're off.
The better way to do all of this, of course, would be to
Load jQuery first
Run your wait() function in a ready() handler so it doesn't run until it's needed.
<script src="jquery.js"></script>
<script src="test.js"></script>
// test.js
$(document).ready(
function()
{
$('#area').text("hello");
}
);
Why doesn't the test.js file get caught in an infinite loop forever checking to see if jQuery has loaded?
setTimeout works asynchronously. It does not pause the browser. It simply asks it to execute a certain function after a certain amount of milliseconds.
jquery.js gets loaded and executed inbetween wait() invocations.
Without that setTimeout() code, when the contents of "test.js" are evaluated the browser will immediately run into the problem of $ (jQuery) not being defined. With the setTimeout(), however, the code does not attempt to use the global jQuery symbols until it verifies that the symbols are defined.
Without the setTimeout the code fails with a runtime error. The code in the other version explicitly tests for that failure possibility to avoid it.
setTimeOut method runs in a separate queue called asynchronous callback. so once the interpreter comes to this line, the code is moved to a separate queue and continues with it parsing(which then executes jQuery.js). After this is executed , it looks for items the asynchronous queue to check if the timeout is completed and then executed method inside setTimeout. By this time jQuery.js is already loaded.
More on this
https://youtu.be/8aGhZQkoFbQ
JavaScript is not pre-compiled. It's working "on the fly".
You can add code on the fly, whenever you want, and this includes loading whole libraries. Once the browser loaded an external JS file it parses it, and it's all ready to use.
So if you wait for jQuery, and do have the proper code to load it, it will eventually be loaded by the browser and work.
Related
I've just learned an important fact about the execution of Javascript in case of an error being thrown. Before I start making conclusions of this I'd better verify whether I am right.
Given an HTML page including 2 scripts:
<script src="script1.js" />
<script src="script2.js" />
script1:
doSomething();
script2:
doSomeOtherThing();
This effectively results in a single script being processed as one unit:
doSomething();
doSomeOtherThing();
In particular, if doSomething throws an error, the execution is broken. 'script2' is never being executed.
This is my "Lesson 1" - one might think since it is a separately included file it is not affected by script1. But it is. => see "late update" below
Now, if we change script2 as follows (presuming we have jQuery included somewhere above):
$(document).ready({ doSomeOtherThing(); });
and place the script before script2:
<script src="script2.js" />
<script src="script1.js" />
The order of execution is effectively still 'doSomething()' followed (sometime) by 'doSomeOtherThing()'.
However it is executed in two "units":
doSomething is executed early as part of the document's java script
doSomeOtherThing is executed when the document.ready event is processed.
If doSomeOtherThing throws an exception, it will not break the second processing "unit".
(I refrain from using the term thread because I reckon that all script is usually executed by the same thread, or more precisely this may depend on the browser.)
So, my Lession 2: Even though a JavaScript error may prevent any subsequent scripts from executing, it does not stop the event loop.
Conclusion 1
$(document).ready() does a great job in defining chunks of JavaScript code that should be executed independent on any other scripts in succeeding.
Or, in other words: If you have a piece of JavaScript and want to make sure it gets executed even if other scripts fail, place it within a $(document).ready().
This would be new to me in that I would only have used the event if the script depends on the document being fully loaded.
Conclusion 2
Taking it a step further it might be a good architecture decision to wrap all scripts within a $(document).ready() to make sure that all scripts are "queued" for execution. In the second example above, if script2.js was included after script1.js as in example 1:
<script src="script1.js" />
<script src="script2.js" />
An error in script1.js would prevent the doSomeOtherThing() from even being registered, because the $(document).ready() function would not be executed.
However, if script1.js used $(document).ready(), too, that would not happen:
$(document).ready(function() { doSomething(); });
$(document).ready(function() { doSomeOtherThing(); });
Both lines would be executed. Then later the event loop would execute doSomething which would break, but doSomeOtherThing would not be affected.
One more reason to do so would be that the thread rendering the page can return as soon as possible, and the event loop can be used to trigger the code execution.
Critique / Questions:
Was I mistaken?
What reasons are there that make it necessary to execute a piece of code immediately, i.e. not wrapping it into the event?
Would it impact performance significantly?
Is there another/better way to achieve the same rather than using the document ready event?
Can the execution order of scripts be defined if all scripts just register their code as an event handler? Are the event handlers executed in the order they were registered?
Looking forward to any helpful comments!
Late Update:
Like Briguy37 pointed out correctly, my observation must have been wrong in the first place. ("Was I mistaken - yes!"). Taking his simple example I can reproduce that in all major browsers and even in IE8, script2 is executed even if script1 throws an error.
Still #Marcello's great answer helps get some insight in the concepts of execution stacks etc. It just seems that each of the two scripts is executed in a separate execution stack.
The way JS handles errors, depends on the way JS is processing the script. It has nothing (or little) to do with threads. Therefor you have to first think about how JS works through your code.
First of all JS will readin every script file/block sequently (at this point your code is just seen as text).
Than JS starts interpreting that textblocks and compiling them into executable code. If an syntax error is found, JS stops compiling and goes on to the next script. In this process JS handles every script blocks/files as separated entity, that's why an syntax errors in script 1 does not necessarily break the execution of script 2. The code will be interpreted and compiled, but not executed at this point, so a throw new Error command wouldn't break the execution.
After all script files/blocks are compiled, JS goes through the code (starting with the first code file/block in your code) and building up a so called execution stack (function a calls function b calls function d and c....) and executing it in the given order. If at any point a processing error occurs or is thrown programmatically (throw new Error('fail')) the whole execution of that stack is stopped and JS goes back to the beginning to the beginning of that stack and starts with the execution of the next possible stack.
That said, the reason that your onload function is still executed after an error in script1.js, does not happen because of a new thread or something, it's simply because an event builds up a separate execution stack, JS can jump to, after the error in the previous execution stack happend.
Coming to your questions:
What reasons are there that make it necessary to execute a piece of code immediately, i.e. not wrapping it into the event?
I would advice you to have no "immediatly" called code in your web-application at all. The best practice is to have a single point of entrance in your application that is called inside of an onload event
$(document).ready(function () {App.init()});
This however has nothing to do with error handling or such. Error handling itself should definitely be done inside your code with either conditionals if(typeof myValue !== 'undefined') or try/catch/finally blocks, where you might expect potential errors. This also gives you the opportunity to try a second way inside of the catch block or gracefully handle the error in finally.
If you can build up your application event driven (not for error handling reasons of course), do so. JS is an event driven language and you can get the most out of it, when writing event driven code...
Would it impact performance significantly?
An event driven approach would IMHO make your application perform even better and make it more solid at the same time. Event driven code can help you to reduce the amount of internal processing logic, you just have to get into it.
Is there another/better way to achieve the same rather than using the document ready event?
As before mentioned: try/catch/finally
Can the execution order of scripts be defined if all scripts just register their code as an event handler? Are the event handlers executed in the order they were registered?
If you register the same event on the same object, the order is preserved.
Your first assumption that they run as one script is incorrect. Script2 will still execute even if Script1 throws an error. For a simple test, implement the following file structure:
-anyFolder
--test.html
--test.js
--test2.js
The contents of test.html:
<html>
<head>
<script type="text/javascript" src="test.js"></script>
<script type="text/javascript" src="test2.js"></script>
</head>
</html>
The contents of test.js:
console.log('test before');
throw('foo');
console.log('test after');
The contents of test2.js:
console.log('test 2');
The output when you open test.html (in the console):
test before test.js:1
Uncaught foo test.js:2
test 2
From this test, you can see that test2.js still runs even though test.js throws an error. However, test.js stops executing after it runs into the error.
I'm not sure about syntax errors, but you can use try {} catch(e) {} to catch the errors and keep the rest of the code running.
Will NOT run untill the end
var json = '{"name:"John"'; // Notice the missing curly bracket }
// This probably will throw an error, if the string is buggy
var obj = JSON.parse(json);
alert('This will NOT run');
Will run untill the end
var json = '{"name:"John"'; // Notice the missing curly bracket }
// But like this you can catch errors
try {
var obj = JSON.parse(json);
} catch (e) {
// Do or don'
}
alert('This will run');
UPDATE
I just wanted to show how to make sure that the rest of the code gets executed in case the error occurs.
What reasons are there that make it necessary to execute a piece of
code immediately, i.e. not wrapping it into the event?
Performance. Every such event fills up the event queue. Not that it will hurt much, but it's just not necessary... Why to do some work later, if it can be done right now? For example - browser detection and stuff.
Would it impact performance significantly?
If you are doing this many times per second, then yes.
Is there another/better way to achieve the same rather than using the
document ready event?
Yes. See above example.
Can the execution order of scripts be defined if all scripts just
register their code as an event handler? Are the event handlers
executed in the order they were registered?
Yes, I'm pretty sure that they are.
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 know it may sound very strange, but I need to know if there is any active/running javascript in the page.
I am in situation in which I have to run my javascript/jquery code after everything on the page is rendered and all other scripts have finished.
Is it possible to detect this?
EDIT:
Thank you all for the answers. Unfortunately, I was not able to find a solution, because I have no full control of what is going on the page.
Even, I was able to put my javascript in the end of the page, I think it will not be solution again. The reason is that when the page is rendering a function is triggered, it calls other functions and they calls other and so on. As a result, some of the data is incorrect and that's why i need to run my code to correct it.
I use setTimeout with 2 seconds to ensure that my code will be executed last, but this is ugly...
So, thank you all, but this is more problem with the system, not the js.
JavaScript on web browsers is single-threaded (barring the use of web workers), so if your JavaScript code is running, by definition no other JavaScript code is running.*
To try to ensure that your script occurs after all other JavaScript on the page has been downloaded and evaluated and after all rendering has occurred, some suggestions:
Put the script tag for your code at the very end of the file.
Use the defer and async attributes on the tag (they'll be ignored by browsers that don't support them, but the goal is to make yours the last as much as we can).
Hook the window load event via a DOM2 style hookup (e.g., addEventListener on browsers with standards support, or attachEvent on older IE versions).
In the load event, schedule your code to run after a setTimeout with a delay of 0ms (it won't really be zero, it'll be slightly longer).
So, the script tag:
<script async defer src="yourfile.js"></script>
...and yourfile.js:
(function() {
if (window.addEventListener) {
window.addEventListener("load", loadHandler, false);
}
else if (window.attachEvent) {
window.attachEvent("onload", loadHandler);
}
else {
window.onload = loadHandler; // Or you may want to leave this off and just not support REALLY old browsers
}
function loadHandler() {
setTimeout(doMyStuff, 0);
}
function doMyStuff() {
// Your stuff here. All images in the original markup are guaranteed
// to have been loaded (or failed) by the `load` event, and you know
// that other handlers for the `load` event have now been fired since
// we yielded back from our `load` handler
}
})();
That doesn't mean that other code won't have scheduled itself to run later (via setTimeout, for instance, just like we did above but with a longer timeout), though.
So there are some things you can do to try to be last, but I don't believe there's any way to actually guarantee it without having full control of the page and the scripts running on it (I take it from the question that you don't).
(* There are some edge cases where the thread can be suspended in one place and then allow other code to run in another place [for instance, when an ajax call completes while an alert message is being shown, some browsers fire the ajax handler even though another function is waiting on the alert to be dismissed], but they're edge cases and there's still only one thing actively being done at a time.)
There is no definitive way to do this because you can't really know what the latest is that other scripts have scheduled themselves to run. You will have to decide what you want to target.
You can try to run your script after anything else that may be running when the DOM is loaded.
You can try to run your script after anything else that may be running when the page is fully loaded (including images).
There is no reliable, cross-browser way to know which of these events, the scripts in the page are using.
In either case, you hook the appropriate event and then use a setTimeout() to try to run your script after anything else that is watching those events.
So, for example, if you decided to wait until the whole page (including images) was loaded and wanted to try to make your script run after anything else that was waiting for the same event, you would do something like this:
window.addEventListener("load", function() {
setTimeout(function() {
// put your code here
}, 1);
}, false);
You would have to use attachEvent() for older versions of IE.
When using this method, you don't have to worry about where your scripts are loaded in the page relative to other scripts in the page since this schedules your script to run at a particular time after a particular event.
A way to know when multiple functions have all finished executing
This can be useful if you have to wait multiple API calls or initialisation functions
let processRemining = 0;
async function f1() {
processRemining++
await myAsyncFunction()
processFinished()
}
async function f2() {
processRemining++
await myAsyncFunction2()
processFinished()
}
function processFinished() {
processRemining--
setTimeout(() => { // this is not needed is all the functions are async
if (processRemining === 0) {
// Code to execute when all the functions have finished executing
}
}, 1)
}
f1()
f2()
I often couple it with a freezeClic function to prevent users to interact with the page when there is a script that is still waiting an ajax / async response (and optionnaly display a preloader icon or screen).
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.
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.