This is a snippet from a file I currently use:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<link id="SKIN.CSS" rel="stylesheet" charset="UTF-8"/>
<script id="APP.JS" type="text/javascript" charset="UTF-8"></script>
<script id="GUI.JS" type="text/javascript" charset="UTF-8"></script>
<title></title>
</head>
<body>
<script>
window.Gui = { };
window.App = { }
( function( ) {
var ajax = new XMLHttpRequest( ),
remains = 3, // Archivos que faltan por cargar.
response,
tmp;
// Solicitamos los archivos a cargar.
ajax.onreadystatechange = onResponse;
ajax.open( 'POST', 'config.php' );
ajax.send( );
function onResponse( ) {
if( ( ajax.readyState == 4 ) && ( ajax.status == 200 ) ) {
response = JSON.parse( ajax.responseText );
// Cargamos el resto de archivos.
tmp = document.getElementById( 'SKIN.CSS' );
tmp.onload = onLoaded;
tmp.href = response.skin;
tmp = document.getElementById( 'APP.JS' );
tmp.onload = onLoaded;
tmp.src = response.app;
tmp = document.getElementById( 'GUI.JS' );
tmp.onload = onLoaded;
tmp.src = response.gui;
}
}
function onLoaded( ) {
-- remains;
console.log( 'remains:', remains );
if( !remains )
setTimeout( Run, 0 );
}
function Run( ) {
console.log( 'Se llamó a RUN( ).' );
Gui.Init( );
setTimeout( App.Run, 0 );
}
} )( );
</script>
</body>
</html>
I make an AJAX call to get the names of 3 files to upload, and set the attributes by handsrc=
and href=
as appropriate, to load them dynamically.
The matter is in
function onLoaded( ) {
-- remains;
console.log( 'remains:', remains );
if( !remains )
setTimeout( Run, 0 );
}
This code, in other languages, would be more than enough reason for direct expulsion from the guild .
Suppose the following sequence of events occurs:
- The
SKIN.CSS
. - The function is called
loaded( )
, which is executed completely. `remains === 2'. - The
APP.JS
. - The function is called
loaded( )
. - Inside
loaded( )
, decrementremains
, which passes to=== 1
. - Before entering the
if
, theGUI.JS
. - It runs
loaded( )
.remains === 0
. - I enter the
if( )
. The condition is met. It runsApp.Run( )
. - The browser continues at point
6
, but now I can enter theif( )
, becauseremains === 0
( it was not when it was interrupted). - is called a second time
App.Run( )
.
Is this sequence of events possible in Javascript, or does the language guarantee that no function will be interrupted by any event?
Javascript runs in a single thread on each browser tab. All browsers implement it in the same way, although some run all javascript in the same thread and others dedicate a thread for each tab.
Code execution is never interrupted by the arrival of a new event. Instead, all events (that have a "listener" or "callback") are added to a queue of pending events to be attended.
JavaScript events are served in an event loop similar to this:
If an event takes too long to process because it is poorly designed (for example doing a synchronous XHR query, falling into an infinite loop, or processing large amounts of data) the browser can display a warning message to the user to allow them to stop the script execution and recover (relatively) the stability of the browser or navigation tab.
Your code will never run into a race condition because each event will be executed in that loop in "run-to-complete" mode .
There may be easier ways to speed up the loading of a page than to slow down the loading of everything, including the style sheet.
proof of concept
First of all, to use the https://www.deelay.me/ load delay service over HTTPS you have to accept the security exception :(
We will start from this example to improve it:
The result can be something like:
The "executed script" is delayed due to the preloading of the style sheet (1s) of the javascript itself that displays the message (2s).
The end of document script ("End of DOM") and "DOM Loaded" follow each other in succession and only "Page Loaded" is delayed an additional second due to the additional three seconds since loading from the stylesheet (they could would have been more if the browser had stopped interpreting the DOM during the javascript load).
Adding
defer
to<script>
Now the script of the end of the document ("End of the DOM") is executed after the loading of the style sheet (1s) without waiting for the download of the javascript with the attribute
defer
.The external script is executed after the two seconds of delay and immediately the loading of the "DOM" is finished.
It will still take another second for the page to load even though the image was queued for download from the start.
We've gained two seconds to "End of DOM" and one second on the rest.
Adding the style sheet at the end
The entire document is parsed and executed almost immediately (but using the default styles) saving three seconds from the proof of concept and one second from delaying the loading and execution of the javascript at the cost of seeing the incorrect style sheets during a few moments.
The execution of the script will be after its load (2s) and the DOM is considered finished (although there is still an image and a style sheet to load, although it will have finished before the image).
As soon as those two finish loading (3s from start for image and 1s from "End of DOM" for stylesheet) the "Page Loaded" is generated.
conclusion
Is it worth doing an XHR to get the stylesheet and javascript to load?
defer
.To speed up the loading of style sheets it is best to make use of the browser's cache or directly include the style sheets on each page on the server side.
In your code it would be enough to wait for the DOM to be loaded (
document/DOMContentLoaded
) to call your functionApp.Run()
, but if you need a style sheet it would wait for the page to load (window/load
).