Gateway Timeouts and Browser Rendering - javascript

If my browser makes a GET to a script src which is currently having a gateway timeout aka 504, why does the browser hang and stop rendering until the response is actually delivered 60 seconds later? Aside from crashing the browser, isn't this the worst thing that could happen to a production javascript application? Is there anything you can do as the app dev to prevent this from blocking the rest of the rendering and script execution?

If the script is inline (e.g. not dynamically loaded) and not marked defer or async, then the script must be processed synchronously in order and the browser cannot proceed without it. Inline <script> tags (without any special attributes) are processed in order as encountered and the browser MUST process them that way.
If you want your page to render without waiting for the script to load, then you can either load it dynamically or you can mark it async or put the <script> tag right before the </body> tag and the page rendering will not wait for it. If using defer or async, you must make sure that no other scripts are dependent upon the loading of this script, otherwise they might run before this one loads.
See these references for more info:
load and execute order of scripts
Script Tag - async & defer

If you are talking about javascript in the DOM within script tags, browsers will always load them synchronously which is why it is important to have the bulk of your JS at the bottom of the page. If this becomes a big issue I would recommend using using an async loading library such as lab.js http://labjs.com/.
If you are getting into more advanced JS and want to utilize something like the AMD pattern for script loading and dependencies you can use http://requirejs.org/.

Related

Load a script synchronously before render (programatically in <head>)

I have a javascript script that I want to load synchronously, but the URL of the script is determined by the value of a cookie. For example:
<script src="/js/[user-id-from-cookie].js">
This script has to load in the <head> of my HTML document and must be run synchronously (it has to block the main thread while it is downloaded and executed).
How can I use javascript to:
Read the value of a cookie
Download and execute the script synchronously
I need to do this in pure javascript, no frameworks like jQuery etc.
Thanks!
You can use document.write to load scripts synchronously.
But use it this way may cause some lag, I'd like to offer some alternatives.
parse the cookie and return different script per user by server. (recommended, but need to have server support)
load all the script, but rewrite them so only one would execute based on condition. (not good for large amount of variant, of course)
hide the body and only show after whatever preprocess have done. (not actually how to load script, but then you can load it asynchronously)

Javascript async defer order of execution in case of multiple files in Single Page Applications

I am trying to improve the page load performance of my page, that is implemented on EmberJS.
I am considering using asyc and defer on our Javascript files. All other optimizations have already been done (moving the scripts to the bottom of the page, adding async and defer to analytics tags etc).
Now, as per ember-cli specs, the generated index.html has two script tags - one vendor JS file and one application JS file.
If I am to implement async and defer, I need to ensure that my vendor JS file is loaded before my application JS file, to make sure the latter has all required code to initialize the application.
I understand that the order in which the scripts are fetched and parsed are different when defined with async and defer, as defined here
My question is this:
If you have multiple JS files in the same page, is there a way to fetch and execute them in a stipulated order? I'm looking for something like callbacks/promises in async requests, but in terms of the actual script tag itself.
Things may have changed for the better since this question was first posted, but it seems that in 2019 you can defer your scripts and have them processed in the order the script tags are written in your html document. Adding defer to both your vendor script and your main script will cause them to load in parallel, not block parsing of the html document, and be processed in order on document parse completion.
The 4.12.1.1 Processing model section of whatwg's scripting document goes into quite a bit of detail that I'll try to summarise here:
If the script's type is "classic" (not type="module"), and 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
then add the element to the end of the list of scripts that will
execute in order as soon as possible associated with the node
document of the script element at the time the prepare a script
algorithm started.
Check out the link for full details, but essentially what it seems to be saying is that deferred scripts will be processed in the order they are parsed in the html document.
MDN agrees:
Scripts with the defer attribute will execute in the order in which
they appear in the document.
One other important point to note (from the same MDN document):
Scripts with the defer attribute will prevent the DOMContentLoaded event from firing until the script has loaded and finished evaluating.
It's also worth noting that neither whatwg nor MDN says anything about placing your script tag in the head or at the bottom of the body of the html document. If all of your scripts have the defer attribute, they will be processed in occurrence order when the html document has completed parsing. Putting the script tags in the header will mean they will start to download early in the html document parsing process, rather than later which is the case when they are placed at the bottom of the body. But of course that also depends on how many other resources you are downloading from the same host in parallel.
Rambling a bit now, but in summary, for best non-blocking performance:
Place all your script tags as early in the html document as possible
Add them in the order that you want them to be processed
Add the defer attribute to all of them (if they don't need to be processed synchronously or as soon as downloaded)
For scripts that need to be processed as soon as downloaded, add the async attribute. HTML parsing will continue while the script is downloading - will pause when the script has finished downloading and while the script is executed - and will resume once the script has finished executing.
For scripts that need to be processed as soon as downloaded, and that have side effects such as modifying the DOM, don't add async or defer. HTML parsing will pause while the script is downloading - will stay paused when the script has finished downloading and while the script is executed - and will resume once the script has finished executing.
Update July 2020:
In Chrome, downloading and parsing of synchronous scripts (those without async or defer) has improved quite a bit. Downloading and parsing are done on separate threads - and the download thread streams the file into the parser as it downloads.
In combination with <link rel="preload"> in your <head>, it's possible that your file could be downloaded by the time the HTML parser reaches your <script> tag - which means it won't need to pause and can execute the script right away:
The image above is taken from the video Day 2: Chrome web.dev Live 2020 - What's New in V8 / Javascript - the section in which they explain updates to downloading and parsing is about 4 minutes long, but well worth the watch.
I can think of two approaches.
a) Do what you said. i.e. have a script tag which has two chained promises inside, each of which creates a new script tag, appends it to the DOM, adds an onload event function which would be the promise's resolve function and lastly sets its src attribute to the resource's URL. When the script from the first promise loads, the second promise should execute and do the same thing.
b) Take the middle road. Have the vendor file in the head, to load synchronously, and have the application file at the very bottom of the document, to load after everything else finished.
In my opinion the first option is an overkill.
EDIT: Example for a)
<script>
var p = new Promise(function(resolve, reject) {
var scriptTag = document.createElement('script');
document.head.appendChild(scriptTag);
scriptTag.onload = resolve;
scriptTag.src = 'URL_to_vendor_file';
});
p.then(function() {
var scriptTag = document.createElement('script');
document.head.appendChild(scriptTag);
scriptTag.src = 'URL_to_application_file';
};
</script>
Note: The example above can be written and without the use of promises

errors prevent embedded javascript from running

I have a service where users embed a javascript code in they website, in the body tag. Sometimes the page where the code is embedded throws a javascript error from other javascript files which prevent our script from running.
Is there a way to design our code so it doesn't interfere with other javascript scope.
The only solution I can think of is to put the js code in an iframe.
The best way to make sure that your code always runs, is to make sure it is always loaded first.
As long as no scripts are dynamically loaded or marked as async or defer, scripts are run or evaluated in the order encountered in the page. So, the first scripts encountered run first.
In other words, by default, script tags are downloaded and evaluated sequentially as they are encountered in an HTML document.
An externally referenced script file that must be loaded will cause all further javascript execution to wait until that externally referenced file is loaded and parsed and runs.
What all of this means is that you should make sure your script is at the very top of the HTML that is being loaded, meaning it will be processed first and other scripts will not have a chance to interfere with yours.
reference: http://docstore.mik.ua/orelly/webprog/jscript/ch12_03.htm

What exactly is the benefit of the HTML5 async attribute on script elements?

I have some confusion around the new async attribute to the script element in HTML5 that I hope someone can give a clear answer to.
Browsers are capable of Parallel Connections, therefore images will be downloaded in parallel. But any external javascript is not downloaded in parallel with other external javascript and images. Scripts block page loading until they have been downloaded and executed.
To download a script without blocking the rest of the page loading, the most common technique is to create a script element, like Google Analytics snippet does:
var ga = document.createElement('script');
ga.type = 'text/javascript';
ga.src = '...ga.js';
ga.async = true;
var s = document.getElementsByTagName('script')[0];
s.parentNode.insertBefore(ga, s);
I'm not sure of how that works exactly -
either
the browser parses and renders the page, then once it has finished it notices the DOM has changed, resulting in the ga.js script being downloaded and executed
or
the browser starts downloading the javascript in parallel with other resources.
I think it is the latter.
The new asynchronous Google Analytics snippet includes the HTML5 async attribute in the script element it creates. That will not help the page blocking problem - that has already been solved by the "Script DOM Element" technique. So what does async add to the picture? According to w3schools, "if async is present, the script is executed asynchronously with the rest of the page (the script will be executed while the page continues the parsing)".
And according to Steve Souders site, "the main benefit of this [async attribute] is it tells the browser that subsequent scripts can be executed immediately – they don’t have to wait for ga.js".
So are async and the Script DOM element technique both solving the same problem?
Will work:
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script>$('body').append('Yey');</script>
Will not work:
<script async src="//ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script>$('body').append('Yey');</script>
The async attribute is just a clearer (no ambiguity very straightforward) and cleaner (it will, or is already, part of the respected HTML5 specification) approach to solve the problem. If your site serves scripts from another domain (or CDN) then the async attribute gives you a little reliability (allow the user to at least read the static content) in that the page won't block while a script from a slow (possibly down) remote host is trying to load.
There was a great article from Jake Archibald on html5rocks which addresses this topic.
According to https://www.html5rocks.com/en/tutorials/speed/script-loading/ if a <script> element is added dynamically it may not be executed until DOMContentLoaded is fired. That is, some user agents (e.g. MSIE 10) will wait until DOM is ready before running dynamically added <script> elements.
I guess Google wants to get their analytics code running faster on those user agents and as such they need to add async flag to tell the browser (e.g. MSIE 10) that it's okay to start executing the script as soon as possible. HTML5 compatible browsers would execute as if the async true even if it was not defined so the async=true has been added only to improve performance with non-HTML5 browsers.
setting async attribute to true makes sure that the script is loaded along with the rendering of html in parallel.This is essential because if script is placed at end of body and in html we are using something that depends on javascript code,so it won't be loaded and creates issue
defer can be used but defer just pauses execution of script and renders html
Async scripts are executed as soon as the script is loaded, so it doesn't guarantee the order of execution (a script you included at the end may execute before the first script file )
Defer scripts guarantees the order of execution in which they appear in the page.

load scripts asynchronosly and have a fallback

My objective here is to load scripts asynchronously when the browser supports defer or async.
If the browser supports neither I don't care about asynchronous loading (not my bad).
I want to make sure that any script is only executed when the prerequisites for it are fulfilled e.g. jQuery loaded.
I want to load my scripts while other scripts are being loaded (only if the browser supports defer or async).
I want to do this using only the browsers' API. I don't want the browser to load any reliable (or not) scripts that do that for me no matter how small they are.
This must work with IE8+, Gecko v.1.9.1+ (e.g. firefox 3.5.* or firefox 9.0+), webkit (e.g. chrome), presto (e.g. Opera). For the ones I didn't mention the version, I mean the latest stable version.
If possible I don't want any non easy scripts. I just need something simple to do this job. This means:
If possible, I don't want stuff like AJAX calls or fancy Objects with some methods to do some workarounds like I've seen in other pages. Those are to force async loading of the script in browsers that do not support async or defer
I repeat: I don't need any fancy things to make a script asynchronous. If the browser does not support defer or async I don't care. I just care is that the script is loaded so that each part is executed after its prerequisites are met and use async or defer if the browser supports it.
First, using a library such as jQuery makes this whole process infinitely easier, and reliable across browsers. It may increase the download size of your pages (by a very small amount) but the speed gained by efficient script loading/executing will nearly always outweigh that.
Regarding script async and defer attributes:
async="async": on a script tag is not supported at all by IE8/9, script executes immediately (which is ok according to your question).
defer="defer": on a script tag will begin loading after everything in the order the defer scripts appear in the HTML, prior to DOM Ready. BUT, on Firefox, scripts will often execute AFTER dom ready. This difference makes defer unreliable as a means of ensuring that scripts are loaded before executing functions after dom ready.
General Guidelines when not using jQuery:
If a script has downstream dependancies you have to place it as a standard script tag at the end of the body tag and have your inline tags all execute after document ready. Otherwise there is no guarantee that the script will be executed prior to the execution of the dependencies. Firefox is the main issue here, a "defer" script may not have finished even after DOM ready.
If a script has no downstream dependnacies, then place it at the end of the body tag, and use async="async" attribute on the script tag. IE will render it immediately and the others will render it when they receive it.
General Guidelines when using jQuery:
Place only jQuery in your <head>.
Execute all other scripts as $.getScript().
If a script needs to execute ASAP (such as analytics) use a $.getScript at the top of the body (this will be a non-blocking request, but will process as soon as the client receives the file).
If a script can wait till DOM ready, wrap the $.getScript() call in $(function() {});
If a script has many downstream dependancies, have each one register itself to the callback function for a specific script.
$(function() {
$.getScript("script.js", function() {
for(var i = 0; i < myCallbacks.length;i++) {
myCallbacks[i]();
}
});
});

Categories