
Managing Async Dependencies with JavaScript - tnorthcutt
https://hackcabin.com/post/managing-async-dependencies-javascript/
======
bfred_it
If your intent is to load scripts asynchronously and keep their order, just
use `<script defer>`. From what I see, you only ever need something like
`fetch-inject` if you also want to load stylesheets OR if you want a load
callback.

I've been using `defer` exclusively ever since I read this:
[https://calendar.perfplanet.com/2016/prefer-defer-over-
async...](https://calendar.perfplanet.com/2016/prefer-defer-over-async/)

~~~
jhabdas
The `defer` attribute waits until the document is finished parsing. Async
doesn't wait.

------
Kholo
Where are the numbers? What is the speed up?

The effort that goes into these posts without addressing the basics always
boggles my mind.

~~~
jhabdas
Like these numbers? [https://hackernoon.com/putting-wordpress-into-
hyperdrive-470...](https://hackernoon.com/putting-wordpress-into-
hyperdrive-4705450dffc2)

------
robocat
For mobile, my timings showed fetch/XHR to be heaps laggier than <script>
tags. This matters for any resources on the critical path for rendering your
page.

I naively thought that it would be quicker to have an inline <script> (coming
first in the <head>) and have the script request JavaScript resources via XHR
(to manage resource dependencies and load failures).

However, the browser decides the priority for resource requests and CPU usage.
I found that XHR requests got low priority and wouldn't even get requested
until after painting delays.

~~~
jhabdas
WRT performance, fetch is a newer standard and still getting ironed out.
Browsers have been using external scripts for years and know how to handle
them efficiently. A downside to using `script` tags however, is they have to
be on the page when the document is loaded, and they block the browser run
loop.

------
tpetry
What is the difference between XHR-inject and FETCH-inject? Isn't it that the
second simply uses the fetch api and is more modern? But everything could be
done with XHR too?

~~~
dak1
Yes, fetch is basically sugar around XHR. This is just XHR injection using a
nice API in the library itself (which some browsers still don't support).

From a user's perspective, you could create the exact same experience using
vanilla XHR (which is exactly what AMD module loading does with `define`).

~~~
hdhzy
> Yes, fetch is basically sugar around XHR.

Believe it or not but currently it's actually the other way around: XHR is
defined in terms of fetch:

> XHR is now defined in terms of fetch (see the calls to fetch in XHR's
> .send()), meaning fetch is lower level than XHR.

Source: [https://jakearchibald.com/2015/thats-so-
fetch/](https://jakearchibald.com/2015/thats-so-fetch/) and
[https://xhr.spec.whatwg.org/#the-
send%28%29-method](https://xhr.spec.whatwg.org/#the-send%28%29-method)

------
z3t4
You can use http push (link preload header) or prefetch to make the browser
download all dependencies async. Then require the modules sync. when they are
needed. The advantages with modules is that they can always be cached! You do
not have to bundle or add compile steps. Just use sync. require. Here's an
example require script:
[https://www.webtigerteam.com/webmodules/require.js](https://www.webtigerteam.com/webmodules/require.js)

~~~
jhabdas
Push has its use cases. It has its distinct drawbacks as well. Consider the
scenario when you want to lazy-load a script based on a user action so you're
not aggressively loading things the user might not need. You may also enjoy:
[https://www.smashingmagazine.com/2017/04/guide-
http2-server-...](https://www.smashingmagazine.com/2017/04/guide-http2-server-
push/)

~~~
z3t4
It's up to the browser to optimize. What I've discovered so far is that push
has low priority and that if a script is required while it's being downloaded
(pushed), the browser (chrome) downloads it twice. The browser could however
be smart about it and just make the required script top priority and only
download it once. The browser could also parse the pushed scripts (chrome
currently doesn't) if the cpu is idle. This opens up for modules on the web,
like in Node.JS, but without the latency penalty. Also there is no need for
the browser to pre-parse the scripts to look for modules when they are already
pushed.

------
darth_mastah
> Others may choose to use a bundler to concatenate all of the JavaScript
> files into one or two packages, and load them asynchronously to avoid these
> problems, typically leading to a SEO-unfriendly Single-Page Application
> primed for Front-end SPOF.

Shortsighted as I am, I struggle to see how fetch-inject guards against SPOF?

Having said that, I can see how this library could be useful for projects with
no bundler.

~~~
jhabdas
It helps guard against SPOF by enabling the application to load in smaller
pieces, helping guard against not just SPOF in the front-end but also SPOF in
the development pipeline when a left pad makes your bundler stop working.

------
jhabdas
For those looking for more in depth info, syntax and use cases, here's a link
directly to the library [https://github.com/jhabdas/fetch-
inject](https://github.com/jhabdas/fetch-inject)

------
GroSacASacs
How does this compete with putting a big script tag at the end of the body in
the HTML that contains all the code necessary ? What settings can make this
way of requiring scripts useful ?

I guess this can be useful, to load extra functionality at run time, in very
big single page application.

~~~
jhabdas
In a large document, or one with scripts embedded throughout, waiting until
the body is finished parsing could take considerably longer than async loading
in the head.

------
jackweirdy
If anyone familiar than I am is reading, I'm not sure I understand how
fetchInject differs from using

<script src=... async defer>

for all scripts - could anyone explain?

~~~
jamescostian
AFAICT using async defer, one cannot express "you need to load X.js and Y.js
before running Z.js, and you need to load X.js and A.js before running B.js".
It looks like fetchInject allows you to express that type of thing.

But how is this different from RequireJS? I think RequireJS uses XHR and I
don't think it supports CSS, but aside from that, what's new? I feel like this
is just reinventing AMD.

~~~
thehandofkwll
Yep, this is the race condition that the author was referring to. RequireJs
also let's you express these dependencies, but will block behind the cssdom
due to the way it injects. Actually, I made a little library which let's you
manage dependencies with script async or defer, and can be considered an
alternative to the authors (neat) solution.
[https://github.com/bohdyone/adm.js](https://github.com/bohdyone/adm.js)

~~~
lewisjoe
Curious question: Can you explain a bit more on "will block behind the cssdom
due to the way it injects"

As far as I know, requireJS doesn't do XHR requests and evals. That sounds
ugly. I think it dynamically injects a script tag and loads the module. I
maybe wrong.

Either way, how's it a blocking one?

So, the only thing that makes fetchInject, differ from requireJS is the non-
blocking way. Is my understanding correct?

~~~
thehandofkwll
This article by an engineer at Google explains it pretty well. My own testing
has shown the same results. [https://www.igvita.com/2014/05/20/script-
injected-async-scri...](https://www.igvita.com/2014/05/20/script-injected-
async-scripts-considered-harmful/)

