Load and execute javascript code SYNCHRONOUSLY

JavascriptLoadSynchronous

Javascript Problem Overview


Is there a way to load and execute a javascript file in a synchronous way just like a synchronous XMLHttpRequest?

I'm currently using a sync XMLHttpRequest and then eval for this, but debugging that code is very difficult...

Thanks for your help!

Update

I tried this now:

test.html

<html>
	<head>
		<script type="text/javascript">
			var s = document.createElement("script");
			s.setAttribute("src","script.js");
			document.head.appendChild(s);
			console.log("done");
		</script>
	</head>
	<body>
	</body>
</html>

script.js

console.log("Hi");

Output: done Hi

So it was not executed synchronously. Any idea to make "Hi" appear first?

Update 2 Other example

test.html (code inside a script tag)

var s = document.createElement("script");
s.setAttribute("src","script.js");
document.head.appendChild(s);
SayHi();

script.js

function SayHi(){
	console.log("hi");
}

Output: Uncaught ReferenceError: SayHi is not defined

Javascript Solutions


Solution 1 - Javascript

If you use this:

function loadScriptSync (src) {
    var s = document.createElement('script');
    s.src = src;
    s.type = "text/javascript";
    s.async = false;                                 // <-- this is important
    document.getElementsByTagName('head')[0].appendChild(s);
}

You can do what you want (although divided up in an additional script file)

test.html (code inside a script tag):

loadScriptSync("script.js");
loadScriptSync("sayhi.js"); // you have to put the invocation into another script file

script.js:

function SayHi() {
     console.log("hi");
}

sayhi.js:

SayHi();

Solution 2 - Javascript

All scripts which are loaded after DOM is ready are loaded asynchronously. The only reason for browser to load them synchronously is function write which can output something. So you can use onload callback of the script element to achieve what you want.

var s = document.createElement("script");
s.setAttribute("src","script.js");
s.onload = function(){
    console.log('Done');
}
document.head.appendChild(s);

Another way is to load js-file via XHR and set code inside the script element:

window.onload = function(){
    var req = new XMLHttpRequest();
    req.open('GET', "test.js", false);
    req.onreadystatechange = function(){
        if (req.readyState == 4) {
            var s = document.createElement("script");
            s.appendChild(document.createTextNode(req.responseText));
            document.head.appendChild(s);
        }
    };
    req.send(null);
}

Solution 3 - Javascript

From a similar question ( https://stackoverflow.com/a/3292763/235179 ):

<script type="text/javascript">
  document.write('<script type="text/javascript" src="other.js"><\/script>');
</script>

<script type="text/javascript">
  functionFromOther();
</script>

Either the code called from the document.write'd script needs to be in it's own <script> or it needs to be in the window.onload() event.

Solution 4 - Javascript

Your scripts do execute synchronously

your code if put together is:

1. create script element
2. set its attribute src
3. set its attribute deferred
4. display done...

this first part stops execution and hands it over to next script

5. script executes and displays Hi

Everything is very much synchronous... In Javascript some code is executed completely until it executes to the last line or hands execution over to internal systems (like XHR or timer).

When one would like to put prepare some parts to execute later on, they prepare it with setTimeout. Even if timeout is shorter than the rest of the code will take that's the time it will execute. After code has finished executing. Example:

// some code
setTimeout(function(){ alert("I'm second alert"); }, 1);
longExecutionTask();
alert("I'm the first alert");

In the above code even if setTimeout is set to execute after 1ms it won't start until the code after it finishes execution which ends with displaying an alert box. The same happens in your case. The first batch of code has to finish executing before anything else can start.

Why you're getting exception (in example 2)

You've added some more code after I've written my answer so here goes some more info.

Adding a script tag will not immediately execute it. Script loading+execution will happen when HTML parser will get to the SCRIPT element you added. It will load it at that point and evaluate/execute its content.

  1. HTML parser starts parsing your document
  2. HEAD is being parsed and its SCRIPT child tag gets parsed and executed. This execution adds one more element to BODY tag that hasn't been parsed yet.
  3. Parser moves on to BODY and parses its content (the newly added SCRIPT tag) which then loads the script and executes its content.

SCRIPT elements get immediately executed only when you they're added after your page has been parsed and is already rendered in browser. In your case that is not the case. The first script executes immediately and the dynamically added one executes when parses gets to it.

Solution 5 - Javascript

You can synchronize asynchronous operations among themself. Create recursive function to represent a loop and call the next operation when previous finish. The following function imports scripts in order with the similar technique. It waits a script to be loaded and if there is no error continues with the next. If an error occur it calls the callback function with the error event and if there is no error - calls the same callback function with null after all scripts are loaded. It also cleans after itself with s.parentNode.removeChild(s).

function importScripts(scripts, callback) {
    if (scripts.length === 0) {
        if (callback !== undefined) {
            callback(null);
        }
        return;
    }
    var i = 0, s, r, e, l;
    e = function(event) {
        s.parentNode.removeChild(s);
        if (callback !== undefined) {
            callback(event);
        }
    };
    l = function() {
        s.parentNode.removeChild(s);
        i++;
        if (i < scripts.length) {
            r();
            return;
        }
        if (callback !== undefined) {
            callback(null);
        }
    };
    r = function() {
        s = document.createElement("script");
        s.src = scripts[i];
        s.onerror = e;
        s.onload = l;
        document.head.appendChild(s);
    };
    r();
}

Solution 6 - Javascript

You can use the "defer" attribute when including the js file.

<script defer="defer" src="contact/js/frontend/form.js"></script>

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionVan CodingView Question on Stackoverflow
Solution 1 - JavascriptheinobView Answer on Stackoverflow
Solution 2 - JavascriptbjorndView Answer on Stackoverflow
Solution 3 - JavascriptJosh JohnsonView Answer on Stackoverflow
Solution 4 - JavascriptRobert KoritnikView Answer on Stackoverflow
Solution 5 - JavascriptUser0123456789View Answer on Stackoverflow
Solution 6 - Javascriptuser1077915View Answer on Stackoverflow