Load js Asynchronously But Execute Synchronously - javascript

The Scenario is that I have lots of js files. Different files are called depending on different platforms.
So I have a question as I want to load files Asynchronously. But the Execution of these files should be done synchronously. Currently I am doing this function call
function loadScriptJs(src) {
Console.log(' Call to file '+src);
var file = document.createElement('script'),
head = document.getElementsByTagName('head')[0];
file.type = 'text/javascript';
file.setAttribute("defer","defer");
file.setAttribute("src",src);
head.appendChild(file);
file.onload = function() {
console.log(src + " is loaded.");
};
}
Now I have a array which has a list of js files being called. I loop through the array and call this function for each src.
I have used document.ready function with in each of the js file. Here I have checked the dependencies of the files and made the sequence optimized. ie The dependent files are loaded later (come later in the array) and the more Parent like files come earlier.
Example:- If I have files A1,B2,C3,D4,E5 ..... like wise. My array is ['js/A1.js','js/B2.js','js/C3.js'...]. When I run this through the loop.
I get the consoles of 'Call to file [filename]' is the same sequence as in the array but consoles of '[filename] is loaded' are not coming in the intended sequence.
Yes I cannot control the loading as its over the internet. But I want to the execution to be synchronous. ie Begin all file execution when the last file completes loading.
Can anyone provide me any advice, suggestion or idea for it.

Dynamically inserted scripts may not adhere to the defer attribute. script tags have an async attribute. By default it's true. Explicitly set it to false.
file.setAttribute("async", "false");
or in plain HTML
<script type="text/javascript" async=false src="A1.js"></script>
You might also need to remove the document.ready method in each script and start the script's logic at the top level, appending each JS script to the bottom of the body tag instead of the header. If multiple scripts register for the ready event before it's fired you do not know what order they will be called in.

Related

JavaScript async & defer: run a script asynchronously

As far as I am aware, in the script element, the async attribute allows the script to download asynchronously (similar to images), while the defer causes the script to wait until the end before executing.
Suppose I have two scripts to include:
<script src="library.js"></script>
<script src="process.js"></script>
I would want them both to proceed asynchronously, and I would want process.js to wait until the end to start processing.
Is there a way to get the library.js script to run asynchronously?
Note
I see there appears to be some discrepancy between different online resources as to the actual role of the async attribute.
MDN & WhatWG suggest that it means that the script should execute asynchronously. Other online resources suggest that it should load asynchronously, but still blocks rendering when it is executed.
I found Sequential script loading in JavaScript which might help you:
(function(){
//three JS files that need to be loaded one after the other
var libs = [
'https://code.jquery.com/jquery-3.1.1.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/jquery-easing/1.4.1/jquery.easing.min.js',
'https://cdnjs.cloudflare.com/ajax/libs/underscore.string/3.3.4/underscore.string.js'
];
var injectLibFromStack = function(){
if(libs.length > 0){
//grab the next item on the stack
var nextLib = libs.shift();
var headTag = document.getElementsByTagName('head')[0];
//create a script tag with this library
var scriptTag = document.createElement('script');
scriptTag.src = nextLib;
//when successful, inject the next script
scriptTag.onload = function(e){
console.log("---> loaded: " + e.target.src);
injectLibFromStack();
};
//append the script tag to the <head></head>
headTag.appendChild(scriptTag);
console.log("injecting: " + nextLib);
}
else return;
}
//start script injection
injectLibFromStack();
})();
Both defer and async affect when a script is executed, not when a script is downloaded. I think the confusion comes from the fact that some documentation is a bit sloppy with terms, and the term loaded is unclear as to whether it refers to the fetching of the resource, or the execution of it.
To get library.js to run asyncronously without waiting for the document to load, use async attribute, and to get process.js to wait until document has been parsed, use defer:
<script src="library.js" async></script>
<script src="process.js" defer></script>
Note that library.js is not guaranteed to run before process.js, although it probably will.

Using require.js to load something before onload

I see this question was asked here
load javascript file before onload with requirejs
But the solution there doesn't fit my situation and so I'm wondering if there is a different solution. I can't build with deps and make my code come last because I'm making a library/utility, not an app.
I'm working on the WebGL-Inspector. It works by wrapping HTMLCanvasElement.prototype.getContext and if it sees a "webgl" it then does its thing wrapping the context and allowing you to inspect it.
The WebGL-Inspector works in 3 modes
As a browser extension
As a loader + large compiled script
As a loader + original source (many many scripts)
To use it in modes #2 or #3 above you just insert
<script src="core/embed.js"></script>
Somewhere in the top of your HTML. Because it loads synchronously~ish it will have wrapped HTMLCanvasElement.prototype.getContext before whatever scripts come after it.
These last 2 modes are mostly for debugging/development of the WebGL-Inspector itself. Especially mode #3 because we can edit the inspector's code and refresh the page immediately to see the result, no build step.
I'm in the process of switching to using AMD for all of the WebGL-Inspector. We're using this because we can use webpack to make #2 but still follow the same dev workflow allowing us it use mode #3 above just change the script tag to
<script src="core/require.js" data-main="core/embed.js"></script>
The problem is this no longer works because whatever other code unrelated to the WebGL-Inspector itself runs before core/embed.js has loaded and so calls someCanvas.getContext before we've had a chance to wrap it.
My current solution is sadly to hack in a delay of 1.5 seconds on whatever demo we're using
<script src="core/require.js" data-main="core/embed.js"></script>
<script>
// wait 1.5 seconds for embed.js to load and pray :(
window.onload = setTimeout(reallyRunWebGLCode, 1500);
...
</script>
The previous non-AMD loader doesn't have this async issue. Somehow it manages to load 60 or so .js files before window.onload fires. Is there a way to get require.js to do the same? Or maybe I need to write my own loader?
To put it another way, the issue is as it is now the user adds a single <script> line and makes no other changes to their code and it works. When they're done they remove the single script line.
Switching to AMD/require.js the user is now required to add a single script and re-write code. In my tests went from
window.onload = myapp;
to
require.config({ baseUrl: "/core" });
require("./embed", myapp);
It's minor but now the app is broken when you remove the <script> tag and has to be put back as it was. Requiring those changes is what I'm trying to avoid if possible. In fact with the original style you don't even have to remove the script tag, just run your app in an environment where the script doesn't exist, it will fail to load and your app will run as normal where as with the require method it will fail if the script doesn't exist.
I can require even more code
if (typeof require === 'function' && typeof define === 'function' and define.amd) {
require.config({ baseUrl: "/core" });
require("./embed", myapp);
} else {
window.onload = myapp;
}
But that's even uglier. We went from adding a single script to requiring modifying your app.
Actually it gets even worse in the app I'm currently testing with. It uses a different loader for itself. When I run require version of the code above it fails because in this case require runs myapp before window.onload (strange). I end up having to do this
var tryRunCount = 0;
function tryRunApp() {
++tryRunCount;
// check that tryRunApp has been called twice so we know
// both onload and require have returned
if (tryRunCount === 2) {
myApp();
}
}
window.onload = tryRunApp;
require.config({ baseUrl: "/core" });
require("./embed", tryRunApp);
That's way way more changes than I want to require users to make.

How to get name of JavaScript file being executed?

I'm working on a project that contains several features, so I'm trying to organize each feature on a separated JavaScript file in such a way that the code can be loaded on demand. I want to standardize the load and execution process so I'm trying to develop a function that can load and execute the JavaScript file if it is not present in memory or just execute it if it is already loaded. One approach would be to make the function to require two parameters, the file name and the name of entry point function, then check if the script is already loaded, if it is just execute the entry point function, or if it is not, then load the script and upon the onload event execute the entry point function, this approach works fine but it requires the main module to know all the entry point function names, which in the future may become a pain in the neck, as a careless programmer may inadvertently duplicate a function name. A much cleaner and safer approach would be to name "main" all the entry point functions, and upon first execution associate that "main" to its script DOM object... more or less like this:
Loaded module:
function(){
function main(){
.
. some code here
.
}
Install(getThisFileName(),main);
}();
Main module:
function Load(fn){
var el,ff,i
el = document.getElementsByTagName("script");
ff = "http://"+window.location.host+fn;
for(i=el.length-1;i>=0;i--){
if(el[i] && el[i].src==ff){
el[i].Main();
return;
}
}
el = document.createElement("script");
el.type = "text/javascript";
el.src = ff;
}
function Install(ff,ep){
var el,i;
el = document.getElementsByTagName("script");
for(i=el.length-1;i>=0;i--){
if(el[i] && el[i].src==ff){
el[i].Main = ep;
ep();
return;
}
}
}
But in order to do so I need to know the name of the JavaScript file being executed, which I don't know if it is even possible. Any Ideas?
The easiest way might be to have included first:
var currentFile;
function getThisFileName() {
return currentFile;
}
And at the beginning of each file at the top simply do:
currentFile = 'thisFileName.js';
This will only work however if your code is executed sequentially, once all the files are loaded currentFile will always be the name of the last file.
And alternative method would be to set currentFile at the start of each function in each file. So while that specific function is being accessed you can call your getThisFileName() and it will tell you the last file run, or the current file being run.
Unfortunately this has a bit of overhead with your code itself.
The following will give you all the scripts as an array.
var allScripts = document.getElementsByTagName("script");
The current file will always be the last one .. so allScripts[allScripts.length-1] will give you the current javascript file. You can access the src attribute from this.
EDIT:
This won't work for asynchronous implementation.
You could globally declare an object like:
var scripts = {
addScript : function(filename) {
scripts[filename] = {
loaded : true,
executed : false
}
}
}
Then at the very bottom of each file call:
scripts.addScript('myFilename.js');
Then at the end of the script's execution call:
scripts['myFilename.js'].executed = true;
Then at any time you can check scripts to see if a particular file is loaded/executed.

Synchronous <script> statements?

I am using xmlhttprequest (and eval) to dynamically load scripts. Next I evaluate the script and look to see if there are any other scripts to load. Along the way if any of the scripts cause and exception, the error message reported indicates the line number associated with eval, not with the actual error in the script.
On another question it was suggested I use <script> instead to get better error messages. Unfortunately, <script> is asynchronous, and I won't be able to control the order of loading of scripts (I need an onload callback).
How do I implement synchronous behaviour in <script> commands
Some more info as to what I aim to achieve
Every script has a list of other scripts it loads, stored in a list, lets call it _toLoad
Lets say we have a script 'Main.js' with a load list like so
_toLoad = [['A.js'] , ['B.js'] , ['C.js'] , ['D.js' , 'E.js' , 'F.js']]
Which states that once loaded, the file 'A.js' must be loaded next. Once 'A.js' is loaded, 'B.js' must be loaded next. Once 'B.js' is loaded, 'C.js' must be loaded next. Once 'C.js' is loaded, 'D.js' ,'E.js' , and 'F.js' must be loaded, in any order.
I could use <script> to load 'Main.js', evaluate it's _toLoad list and start loading the other scripts, in the correct order. But what happens if 'A.js' has several scripts it also loads? I want those to load in the background, and not to delay 'B.js' from loading
What if 'A.js' has a load list like so:
_toLoad = [['A2.js'] , ['B2.js'] , ['C2.js'] , ['D2.js' , 'E2.js' , 'F2.js']]
Is I would have to go through and issue <script> statements for those. It seems like a depth first approach, when I want a form of breadth first.
One of the linked questions mentioned setting the innerHTML of the script tag element. I think this might do the trick
function load(toEval, callback){
__global_callback = callback;
var node = document.createElement('script');
node.innerHTML = toEval + '; __global_callback()';
document.getElementsByTagName('head')[0].appendChild(node);
}
load("console.log('before...');", function(){
console.log('...and after');
});

What's the best way to execute something only when all my JavaScript is loaded using jQuery?

I'm developing a web page using jQuery, and I want it to execute some code only after ALL my JavaScript files are fully loaded. The head section of my HTML has the following script.
<script src="supervisor/lib/jquery-1.6.min.js" type="text/javascript"></script>
Inside jQuery file, I inserted the following code:
$.getScript('functions.js', function () {
// code here
});
The file functions.js has the following code:
$.getScript('mask.js');
$.getScript('validations.js');
$.getScript('lotsofscripts.js');
// and more...
I want the code here in the first $.getScript() to execute only after ALL the other JS are loaded, but this is not ocurring. What's the best way to achieve this?
PS: I'm using lots of $.getScript() because I find easier to separate them, but I want them to be inserted inside the same file.
You could always just increment a counter. That way your getScript calls remain asynchronous, as the last thing you want to do is change that. And frankly, any packaged solution you find to loading the scripts in parallel and then executing some function afterward will probably just be a shinier version of this:
var counter = 0;
var filesToLoad = ["mask.js", "validations.js", "lotsofscripts.js"];
var filesCount = filesToLoad.length;
// Increment counter each time a getScript completes
function incrementCounter() {
if (++counter === filesCount) {
// This code will execute after everything loads
}
}
// Iterate through the files and run getScript on them,
// with incrementCounter as the callback
for (var i = 0; i < filesCount; i++) {
$.getScript(filesToLoad[i], incrementCounter);
}
Here's a jsFiddle example.
Assuming you know the name of a function defined in a js script that needs to be tested for whether or not it has loaded...
I use Underscore.js isFunction() to figure this out ( http://documentcloud.github.com/underscore/#isFunction )
Example, if script.js contains a function myScriptFunction(), you can write a function that checks:
if (_.isFunction(myScriptFunction)) {
// script.js is loaded
// OK to move on to the next step
} else {
// script.js is not loaded
// check again later
}
I have tried binding to events to figure out if a js script file is loaded, but it doesn't seem to work across all the browsers.
I would suggest HeadJS to load your JS files. You can execute specific code upon completion of specific files or groups of files. Take a look, it's a great little project.
You might want to use jQuery.ajax() instead. You can set the async option to false (it's true by default when you use getScript

Categories

Resources