Should rails apps use 'defer' when loading application.js?

Any recommendations on whether defer should be used for application.js in Rails apps?

How would turbolinks interact?

I’m considering whether or not to add this feature our new gem:

react_on_rails gem

(ps any feedback on the gem would be greatly appreciated!)

Advice for Rails:

Some overall advice:

I got this answer from the Rogues Parley forum:

http://parley.rubyrogues.com/t/should-rails-apps-use-defer-when-loading-application-js/3395/2

joonty

I think the answer to the Rails-specific Stack Overflow question is a bit dismissive, and suggests they didn’t really understand the question (or the defer/async attributes).

In contrast, the accepted answer to the other SO question is excellent, and very clearly explains the benefits of defer/async. I would imagine it’s perfectly safe to use them with application.js, as it doesn’t stop JS running on document load, but it also doesn’t block the page loading or rendering.

I have to say, I haven’t been doing this :blush: but I will from now on.

I got this from Nate Berkopec on the parley.rubyrogues.com forum:

I know nothing about React, can you clarify a few statements?

If the head script includes “defer” in the application.js (which it should!), then we need to include defer in the body scripts!

What head scripts and body scripts are you referring to in the react_on_rails gem?

If I understand your question correctly, if someone is using defer on their application.js, you think your React script tags should also use defer so that they will be executed after application.js. Unfortunately, this won’t work for you, for two reasons.

Incorrect and not-always-available implementations: defer is dangerous - it isn’t implemented across all browsers, and some browsers have buggy implementations (IE9) that don’t work exactly correctly.

defer’s promise is that a script tag with defer set will execute after the document has finished parsing. Again, however, this not entirely guaranteed across all browsers. A real danger here is that you might assume that these scripts will execute in order, that is, a script tag with defer that comes before another one in the document will be executed first. This cannot be assumed, even though this is how you would think it would work in the spec, because this behavior was implemented incorrectly by IE9 (and possibly other browsers).

Defer doesn’t work with inline scripts. defer and async are typically used for script tags with a src attribute (i.e., not inline), and MDN says that Firefox will ignore defer on scripts without a src attribute. In fact, re-reading the HTML spec now, I notice the following: “The defer and async attributes must not be specified if the src attribute is not present.” So inline script will always be executed before a deferred external script.

Hopefully that answers your question for your particular project.

If you’re purely looking for a performance gain - that is, to speed up page rendering by unblocking the document parser, it is far better to be looking at async. Async unblocks the document parser (as explained in your links), but will also execute the script as soon as it is ready. You may be thinking: “Well, that script might be ready before the document is finished parsing!” That’s correct - async may still cause execution while the document is parsing, delaying load. However, since async makes no guarantees about when the script will be executed (which is really what defer was doing all along, although it told you something different!), I think it’s better for us to recommend it’s use over defer.

Finally, the linked StackOverflow answer also does not mention the browser preloader, something we’ve had in all major browsers for a few years now. What the answer fails to mention is that at step 4 (when the browser parser stops while a script downloads), a speculative lookahead parser called the browser preloader is sent ahead in the document to look for resources and download them. This makes a lot of the discussion regarding “where in the document a script tag should go” mostly irrelevant for modern browsers, especially when that script tag has async or defer set. Here’s link to my blog about browser preloaders and other ways to prefetch resources2.

Follow-up response from Nate:

Both async and defer will do absolutely nothing if a script tag does not have a “src” attribute. Scripts without “src” attributes (i.e inline or not external) will always be executed straight away by the parser as soon as they are encountered. Anywhere else, I prefer using “async” along with “defer”. If a browser doesn’t support “async”, it will fallback to “defer”. All of this is behavior straight from the HTML Standard and implemented consistently across browsers.