Dynamically loaded js, mutation observer and promises for nested js loading - javascript

I am working on a mechanism to dynamically load a set of js files that have some dependencies between them (e.g., my code after bootstrap after jquery). I defined a nested json type structure with the dependency relationships and then invoke a Promise based loadscript mechanism (thanks to some of the posts here) that makes async calls to load js files that are at the same level and invokes a recursive loadscript for js files that have dependencies between them.
The files are all correctly appended to HEAD in the order that i expect them to be. For the dependencies, i invoke loading a file for which there is a dependency in the onload handler of its predecessor. However, this does not seem to guarantee that the loaded js code is available to use (i.e., did not seem that the append had completed and the code been executed - e.g., var/const definitions were not available at the time of onload being invoked based on break points, etc... in debugger - i guess this seems correct).
So, added a mutationobserver that observed that the files were added to the DOM (e.g., childList change to HEAD). However, it appears that the mutation is invoked before the code is actually available for use.
Why do i think this? i put break points, console logs with timing info, etc.. at the point where the mutationObserver indicated that a change corresponding to the insertion of the newly loaded script had occurred (e.g., the observer firing because of the script added) but at that point, the code (e.g., some var definition) was not available in the globals in the debugger.
So, the question is not specific about my code but more about whether it is possible to know that the dynamically included js code has run and is available to use. For my own code i can handle this through other mechanisms but for third party js files i do not want to touch them.
So, more specifically, it does not seem that the actual appending of code to the DOM HEAD at the point detected by the mutationObserver actually means that the code has been executed and that it seems from other posts (e.g., Execution of dynamically loaded JS files or load and execute order of scripts ) that the actual execution of the code is somewhat asynchronous depending on what the browser is actually doing.
Any thoughts on the overall process understanding would be greatly appreciated.
Thanks,

Related

When NOT to use defer attribute

I thought I knew how to use 'defer' attribute when referencing external scripts from my HTML pages.
I even thought there is no reason for me NOT to use it. But after a couple of unexpected things I started to research (even here) and I think I'm not 100% sure when it's safe to use it every time I use the script tag.
Is there somewhere a list of known use cases when defer should NOT be used?
The only thing defer does is run your script when the DOM has finished parsing, but before the DOMContentReady event is fired off.
So: if your code does not depend on the DOM (directly, or indirectly through access to document properties that can only be determined once the DOM is done), there is no reason to defer. For example: a utility library that adds a new namespace ComplexNumbers, with a ComplexNumber object type and associated utility functions for performing complex number maths, has no reason to wait for a DOM: it doesn't need to be deferred. Same for a custom websocket library: even if your own use of that library requires performing DOM updates, it does not depend on the DOM and doesn't need defer.
But for any code that tries to access anything related to the DOM: you need to use defer. And yes: you should pretty much have defer on any script that loads as part of the initial page load, and if you did your job right, none of those scripts interfere with each other when they try to touch the various pieces of the DOM they need to work with.
In fact, you should have both defer *and* async, so as not to block the page thread. The exception being if you're loading a type="module" script, in which case you don't get a choice in deferral: it's deferred by default. but it'll still need async.

Script File Executing After Inline Script With CDN or External Domain On HTML injection

I am having an issue with HTML injection into an already loaded DOM where the inline javascript is being loaded after the script file is downloaded. From what I know this should not be async and the inline script should execute after the script file. This works if the domain name is the same as the calling page, but using a CDN or even a subdomain does the same thing. Is there something I should do to rework how I am calling these? I swear this worked before as I had the CDN on for over a week but maybe I never caught this issue.
Console
Loading Inline Script
VM1400:3 Uncaught TypeError: Cannot read property 'init' of undefined(anonymous function)
app.members.event.js?v=204&_=1453644424985:5 Loading Script File
app.members.event.js?v=204&_=1453644424985:71 Finished Script File
Javascript
<script type="text/javascript" src="https://test.azureedge.net/Areas/Directors/scripts/app.members.event.js?v=204"></script>
<script type="text/javascript">
console.log('Loading Inline Script');
app.viewModel.members.event.init();
console.log('Finished Inline Script');
One way is to use jquery's getScript() function.
But preferably, you may use native javascript to load the script file and then run the inline script.
Maybe i have not understood the question clearly.
Edit:
This is a quote from the HTML5 spec regarding script elements.
If the element has a src content attribute, run these substeps:
Let src be the value of the element's src attribute.
If src is the empty string, queue a task to fire a simple event named
error at the element, and abort these steps.
Resolve src relative to the element.
If the previous step failed, queue a task to fire a simple event named
error at the element, and abort these steps.
Do a potentially CORS-enabled fetch of the resulting absolute URL,
with the mode being the current state of the element's crossorigin
content attribute, the origin being the origin of the script element's
Document, and the default origin behaviour set to taint.
The resource obtained in this fashion can be either CORS-same-origin
or CORS-cross-origin. This only affects how error reporting happens.
For performance reasons, user agents may start fetching the script (as
defined above) as soon as the src attribute is set, instead, in the
hope that the element will be inserted into the document (and that the
crossorigin attribute won't change value in the meantime). Either way,
once the element is inserted into the document, the load must have
started as described in this step. If the UA performs such
prefetching, but the element is never inserted in the document, or the
src attribute is dynamically changed, or the crossorigin attribute is
dynamically changed, then the user agent will not execute the script
so obtained, and the fetching process will have been effectively
wasted.
Then, the first of the following options that describes the situation
must be followed:
If the element has a src attribute, and the element has a defer
attribute, and the element has been flagged as "parser-inserted", and
the element does not have an async attribute The element must be added
to the end of the list of scripts that will execute when the document
has finished parsing associated with the Document of the parser that
created the element.
The task that the networking task source places on the task queue once
the fetching algorithm has completed must set the element's "ready to
be parser-executed" flag. The parser will handle executing the script.
If the element has a src attribute, and the element has been flagged
as "parser-inserted", and the element does not have an async attribute
The element is the pending parsing-blocking script of the Document of
the parser that created the element. (There can only be one such
script per Document at a time.)
The task that the networking task source places on the task queue once
the fetching algorithm has completed must set the element's "ready to
be parser-executed" flag. The parser will handle executing the script.
If the element does not have a src attribute, and the element has been
flagged as "parser-inserted", and either the parser that created the
script is an XML parser or it's an HTML parser whose script nesting
level is not greater than one, and the Document of the HTML parser or
XML parser that created the script element has a style sheet that is
blocking scripts The element is the pending parsing-blocking script of
the Document of the parser that created the element. (There can only
be one such script per Document at a time.)
Set the element's "ready to be parser-executed" flag. The parser will
handle executing the script.
If the element has a src attribute, does not have an async attribute,
and does not have the "force-async" flag set The element must be added
to the end of the list of scripts that will execute in order as soon
as possible associated with the Document of the script element at the
time the prepare a script algorithm started.
The task that the networking task source places on the task queue once
the fetching algorithm has completed must run the following steps:
If the element is not now the first element in the list of scripts
that will execute in order as soon as possible to which it was added
above, then mark the element as ready but abort these steps without
executing the script yet.
Execution: Execute the script block corresponding to the first script
element in this list of scripts that will execute in order as soon as
possible.
Remove the first element from this list of scripts that will execute
in order as soon as possible.
If this list of scripts that will execute in order as soon as possible
is still not empty and the first entry has already been marked as
ready, then jump back to the step labeled execution.
If the element has a src attribute The element must be added to the
set of scripts that will execute as soon as possible of the Document
of the script element at the time the prepare a script algorithm
started.
The task that the networking task source places on the task queue once
the fetching algorithm has completed must execute the script block and
then remove the element from the set of scripts that will execute as
soon as possible.
Otherwise The user agent must immediately execute the script block,
even if other scripts are already executing. Fetching an external
script must delay the load event of the element's document until the
task that is queued by the networking task source once the resource
has been fetched (defined above) has been run.
From this I think that your "external" file is loaded after the inline script block. I would therefore use the "getScript()" function from jquery to make sure that the script is loaded before the inline script block.
This is a common problem in the injection scenerio. It occurs due to the variable delays in script availability, as well as due to the parallel and differing implementations on various browsers.
There are 3 options, depending on whether the source code is available for editing or not, and if more than 2 dependencies exist between the script files.
Option 1. Using defer attribute in the script tag
This option can be used, if both scripts are remote (ie, not inline)
"defer" indicates to the browser that the script has to execute after the document has been parsed (quoted from MDN). This is applicable only for remote (not inline) scripts, that have the "src" attribute.
https://html.spec.whatwg.org/multipage/scripting.html#attr-script-defer
You can use it like below.
Defer is supported on major browsers, and I validated on Chrome, Firefox, Webkit on Tizen, and Safari:
https://developer.mozilla.org/en/docs/Web/HTML/Element/script#Browser_compatibility
To provide concrete example of the above cases, refer below. Note that the below have been validated on Firefox, Chrome, IE11, Safari on iPhone, and Webkit on Tizen.
Case 1:
Many Javascript files - All independent:
If there is no dependency, the "defer" attribute allows the HTML to be loaded quickly. The script takes over after downloading, without issues (assuming onload etc are taken care).
Case 2:
Two javascript files test1.js and test2.js - One dependent on the other:
If test2.js is dependent on the loading of test1.js, then the script tag for test2.js "only" should have the defer attribute.
This usage is shown in
http://www.gpupowered.org/loadtest/2_defer.html
Incorrect usage is shown in
http://www.gpupowered.org/loadtest/no_defer.html (Both scripts do not have defer tag - this fails)
http://www.gpupowered.org/loadtest/all_defer.html (Both scripts having defer tag - this also fails)
Async usage that does not work is at,
http://gpupowered.org/loadtest/2_async.html (this fails)
Where does "defer" not meet the needs ?
If the functionality is split across several JS files (say n), and all "n-1" need to be downloaded before "n"th file can start processing some variables, even though the "defer" attribute might be present on all the script source tags, it is rendered irrelevant because the order in which they are received are indeterminate.
More background on defer and the various options for deferred loading (doesnot cover the multi defer case)
http://www.html5rocks.com/en/tutorials/speed/script-loading/
https://developer.mozilla.org/en/docs/Web/HTML/Element/script
Option 2: Using state variables
This option can be used if some additional state variables can be added to both javascript source files.
The approach relies on a named variable in the dependent js file, and a named function in the js file that uses the dependent file. If the dependent file is not loaded at the time the user file is trying to access its functionality, it exits and will be called back when it is really loaded.
This is demonstrated in the below html file.
http://gpupowered.org/loadtest/variable.html (works correctly)
This option does not work if the loading needs to happen repetitively (ie, loading of multiple files of same name etc).
Option 3: Native script loader
In this case, there are multiple javascript files having dependencies between each other.
There is no solution for this case using defer or async or other specification provided tags. For the use-case I had in the remote labs in gpupowered.org, I had to implement my own native script loader using XMLHttpRequest, and the source for this is provided in the link below. This uses worker threads as some of the textures I have are fairly big. The callback function can be used to implement the dependency logic as per the application needs. For example, keep count of all loaded scripts and then trigger full execution etc.
https://github.com/prabindh/gpupowered.gl/blob/master/worker/worker_object_loader.js
The jquery script loader uses the HTTP request as well, though I have not checked if it uses a worker for loading.
https://api.jquery.com/jquery.getscript/
I have two theories:
Might be that there's something in the external script that's delaying the creation of the app.viewModel.members object (either a timeout or an event handler that takes a while to fulfil). This can be easily tested by setting a long timeout in your inline script (f.i. 5000+ms) and then checking if the models object exists.
There's something funky going on when loading a same origin script.
In this case you could try is delay the execution of your inline script by doing the following:
<script type="text/javascript">
document.addEventListener("DOMContentLoaded", function(event) {
app.viewModel.members.event.init();
});
</script>
or just put your inline code in an external .js file and call it with the 'deferred' flag:
<script type="text/javascript" src="https://test.azureedge.net/Areas/Directors/scripts/app.members.event.js?v=204"></script>
<script type="text/javascript" src="{link-to-external-js-file}" defer></script>
function onloadCallback(){
app.viewModel.members.event.init();
}
Use this:
<script type="text/javascript" src="https://test.azureedge.net/Areas/Directors/scripts/app.members.event.js?v=204&onload=onloadCallback"></script
<script type="text/javascript">function onloadCallback(){
app.viewModel.members.event.init();}</script>

Executing code before document.ready in requirejs

I see that require.js has support to execute code when document.ready() is fired. see: http://requirejs.org/docs/api.html#pageload
I was just wondering is there any way to get it to 100% execute code before document.ready() is invoked? For example, does the code main.js get executed before document.ready() event? Thanks.
There is no pure RequireJS solution to your request. In essence what you are asking for is:
I'd like a) to load my modules asynchronously but b) I want to load them synchronously.
Requirement a is entailed by the fact that you are using RequireJS. RequireJS is a loader designed to load AMD modules. AMD stands for asynchronous module definition. If you use RequireJS, you have to be okay with the fact that it operates asynchronously. So it cannot guarantee that any module will load and execute before the document is ready.
Requirement 'b' is entailed by your requirement that the code executes before the document is ready. This cannot be done otherwise than by forcing the execution of your modules to be synchronous. (I've given some thought about ways to delay the ready event but I don't see this as being generally possible. Solutions like jQuery.holdReady are not really delaying the event but only delaying those handlers that were set using jQuery.ready. The event still happens whenever the browser decides it happens but the handlers get fired later. And only the jQuery handlers are affected, nothing else.)
You can load your code using script elements (that don't use the async attribute) but then you won't be using RequireJS.
A solution that would give you something similar to RequireJS would be to use Almond. Your code would have to be built into a single bundle. And you could load this bundle synchronously. It would essentially have the same effect as using script elements but you would still benefit from modularization. Note that Almond comes with a series of limitations. You'll have to decide for yourself whether these limitations are a problem for you. I've explained how to use Almond synchronously in this answer.

Asynchronous JavaScript Download and coupling with inline script

There are several techniques to download external javascript asynchronously, and there are several techniques for coupling asynchronous download with inline code to preserve execution order.
But I have a couple of questions that I'd be most grateful if someone with a good understanding of this area was able to answer please.
The first question is more just checking that controlling the execution of inline code is as obvious as I think it is.
Say I have this:
inline-script-1 (script I have no control over)
inline-script-2 (my script)
where inline-script-2 is code that I can add to a page, but inline-script-1 is not under my control (please, no 'why not' type questions to this - it's just the way it is for reasons I won't bloat this post any further with).
Say inline-script-2 cannot be executed before [asynchronously downloaded] external script A.js has loaded. That's fine - create a script tag with src=A.js and insert it into the document, and also change inline-script-2 to be executed on the onload/onreadystatchange of A.js. So I now have:
inline-script-1
create 'script' element
set the script's src attribute
set the script's onload/onreadystatechange handlers to functions that call a function in A.js - A-func1()
dynamically add script to page
Now when the asynchronous download of A.js is finished, A-func1() will be called.
But if I want to ensure that inline-script-1 has run before inline-script-2, then is that guaranteed just from the order in the page? Do all browsers parse the page in a strict top to bottom way, therefore guaranteeing that inline-script-1 will be executed before the call to the function A-func1() ?
The second question is how to include more than one block of inline code on a page. For example:
inline-script-1 (script I have no control over)
inline-script-2 (my script)
inline-script-3 (script I have no control over)
inline-script-4 (my script)
I can't really employ the same strategy as in the first question because I'd imagine I would not want to reference the external script twice as it may then be downloaded (ignore caching) and executed twice.
inline-script-1
(function() {
create 'script' element
set the script's src attribute
set the script's onload/onreadystatechange handlers to functions that call a function in A.js - A-func1()
dynamically add script to page
})();
inline-script-3
inline-script-4: <not sure what to do here>
I can't just call functions in A.js above as I don't know it has loaded yet. I don't really want to add a script onload handler at that point either because it means polluting the global namespace and there is no guarantee that I wouldn't miss the event being fired anyway depending on how long inline-script-3 takes.
And I can't really repeat the anonymous function block again for the reasons already mentioned.
Also, inline-script-3 may get run before the call to A-func1()
Is there any way to preserve execution order in this case? I'm wondering if I can maybe make use of the new async attribute? If I set the async attribute to false in the created script element in the anonymous function, and leave the inline-script-4 as inline code but also add the async with value false attribute, might that work?
Thanks,
Paul

Does the ORDER of javascript files matter, when they are all combined into one file?

In todays modern age, where lots of (popular) javascripts files are loaded externally and locally, does the order in which the javascripts files are called matter especially when all local files are all combined (minified) into one file?
Furthermore, many claim that Javascript should go in the bottom of the page while others say javascript is best left in the head. Which should one do when? Thanks!
google cdn latest jquery js | external
another cdn loaded javascript js | external
TabScript ...js \
GalleryLightbox ...js \
JavascriptMenu ...js \
HTMlFormsBeautifier ...js > all minified and combined into one .js file!
TextFieldResize ...js /
SWFObjects ...js /
Tooltips ...js /
CallFunctions ...js /
Order matters in possibly one or more of the following situations:
When one of your scripts contains dependencies on another script.
If the script is in the BODY and not the HEAD.. UPDATE: HEAD vs BODY doesn't seem to make a difference. Order matters. Period.
When you are running code in the global namespace that requires a dependency on another script.
The best way to avoid these problems is to make sure that code in the global namespace is inside of a $(document).ready() wrapper. Code in the global namespace must be loaded in the order such that executed code must first be defined.
Checking the JavaScript error console in Firebug or Chrome Debugger can possibly tell you what is breaking in the script and let you know what needs to be modified for your new setup.
Order generally doesn't matter if functions are invoked based on events, such as pageload, clicks, nodes inserted or removed, etc. But if function calls are made outside of the events in the global namespace, that is when problems will arise. Consider this code:
JS file: mySourceContainingEvilFunctionDef.js
function evilGlobalFunctionCall() {
alert("I will cause problems because the HTML page is trying to call " +
"me before it knows I exist... It doesn't know I exist, sniff :( ");
}
HTML:
<script>
evilGlobalFunctionCall(); // JS Error - syntax error
</script>
<!-- Takes time to load -->
<script type="text/javascript" src="mySourceContainingEvilFunctionDef.js"></script>
...
In any case, the above tips will help prevent these types of issues.
As a side note, you may want to consider that there are certain speed advantages to utilizing the asynchronous nature of the browser to pull down resources. Web browsers can have up to 4 asynchronous connections open at a time, meaning that it's quite possible that your one massive script might take longer to load than that same script split up into chunks! There is also Yahoo Research that shows combining scripts produces the faster result, so results vary from one situation to another.
Since it's a balance between the time taken to open and close several HTTP connections vs the time lost in limiting yourself to a single connection instead of multiple asynchronous connections, you may need to do some testing on your end to verify what works best in your situation. It may be that the time taken to open all of the connections is offset by the fact that the browser can download all the scripts asynchronously and exceed the delays in opening/closing connections.
With that said, in most cases, combining the script will likely result in the fastest speed gains and is considered a best practice.
Yes, depending very much on what you do.
For example, if a.js had...
var a = function() {
alert('a');
}
...and b.js had...
a()
...then you wouldn't want to include b.js before a.js, or a() won't be available.
This only applies to function expressions; declarations are hoisted to the top of their scope.
As for whether you should combine jQuery, I reckon it would be better to use the Google hosted copy - adding it to your combined file will make it larger when there is a great chance the file is already cached for the client.
Read this post from the webkit team for some valuable information about how browsers load and execute script files.
Normally when the parser encounters an
external script, parsing is paused, a
request is issued to download the
script, and parsing is resumed only
after the script has fully downloaded
and executed.
So normally (without those async or defer attributes), scripts get excuted in the order in which they are specified in the source code. But if the script tags are in the <head>, the browser will first wait for all scripts to load before it starts executing anything.
This means that it makes no difference if the script is splitted into multiple files or not.
If I'm understanding your question I think you're asking if it matters where in a file a function/method is defined, and the answer is no, you can define them anywhere in a single source file. The JavaScript parser will read in all symbols before trying to run the code.
If you have two files that define variables or functions with the same name, the order that they're included will change which one actually is defined

Categories