Back in the early days of web development, I picked up a bit of folk wisdom that, for code like this
<script src=".../program1.js"></script>
<script src=".../program2.js"></script>
the browser would pause, load the javascript, compile it, execute it, move on to the next script
tag, and repeat. In this way, the browser would work its way through all the javascript on a page and treat it as one linear program.
However, in the brave new modern javascript world, we have asynchronous loading via the async
attribute
<script src=".../program1.js" async></script>
<script src=".../program2.js" async></script>
It's my understanding that this is a good thing because now the browser does not need to pause, download the script, and execute it. Instead it starts downloading the script, but will continue to parse the DOM. i.e. the web page no longer blocks while waiting for javascript to download. (if this is not true I'd appreciate the correction).
However, what's less clear (and harder to test) is how these two programs interact. They appear to run in the same shared space (i.e. javascript is still, from a userland perspective, single threaded with two (global, function) scopes). However, what order they execute in seems ill-defined in the documentation I've read.
I've read through the MDN article on Concurrency model and Event Loop. While interesting and useful, it doesn't quite answer my question. From what I gather, when the browser loads program1.js
or program2.js
, javascript will add a message to the event queue, and that message will be processed as the javascript engine runs through the event loop.
What's missing for me is -- what does that message say? Is it a single message for each program that says "compile and execute all this javascript code"? Or does each program create multiple messages -- in my mind that might look something like
And what happens when the browser is in the middle of processing program1.js
, but finishes downloading program2.js
? Is it possible that execution of statements from each program will be interleaved?
I realize that, as a client developer, the best practice here is to not rely on global scope and write each program and function so it doesn't matter how it's called, and doesn't block other people's code. However, I spend a lot of time dealing with other people's code, some of which isn't well behaved. I'd like to understand what's going on behind the scenes, or if this is behavior that's undefined and, engine independent, and won't line up between implementations.
An async function can contain an await expression, that pauses the execution of the function and waits for the passed Promise's resolution, and then resumes the async function's execution and returns the resolved value. You can think of a Promise in JavaScript as the equivalent of Java's Future or C# 's Task.
An async function can handle a promise called within it using the await operator. await can be used within an async function and will wait until a promise settles before executing the designated code. The await operators here ensure that the data is not logged before the request has populated it with data.
JavaScript Async Functions Async and await are built on promises. The keyword “async” accompanies the function, indicating that it returns a promise. Within this function, the await keyword is applied to the promise being returned. The await keyword ensures that the function waits for the promise to resolve.
Asynchronous JavaScript: Asynchronous code allows the program to be executed immediately where the synchronous code will block further execution of the remaining code until it finishes the current one.
There are two articles illustrate the "async" and "defer" properties in a practical sense (I'm green on the browser internals):
From 2014 with excellent + simple graphics: http://www.growingwiththeweb.com/2014/02/async-vs-defer-attributes.html
From 2016 why async might be an anti-pattern: http://calendar.perfplanet.com/2016/prefer-defer-over-async/
To your question, what "async" instructs the browser to do is:
Effectively "async" can still block rendering and subsequent execution, but it allows the parser to continue working right up until execution can begin.
Multiple async scripts have no guarantee what order they will execute in. Depends how quickly the load. That lets AMD-systems like RequireJS define dependencies and callbacks to load resources async but queue their execution until the "global" environment contains all the prerequisites and the execution order can be negotiated (by rune magic) hopefully.
Defer behaves like this:
On one hand, "defer" is faster because it can never block the parser or rendering. But "defer" can be slower because it must wait to execute until the pipeline is clear.
That sounds like "async" is always better, but if you load 2 MB of JS on weak-CPU phone with a fast connection you may end up waiting 10s for execution before the parser is allowed to finish rendering. Using "defer" would prevent that at the expense your interaction layer being delayed.
The distinction is fuzzier if you're talking client/server or client-side apps. It may be more beneficial to use defer in a backend heavy app like Magento where rendering is handled server-side.
In a fully client-side app you probably get zero content until the JS monolith is loaded, so "defer" doesn't really do anything for you, but then neither does "async" if your whole app is a massive JS bundle and there's nothing to do in parallel.
Is it possible that execution of statements from each program will be interleaved?
No, absolutely not. JS is still single-threaded, and one program runs after the other (although which of them comes first might be unknown).
What does that event loop message say?
The message is a ScriptEvaluationJob. Assuming parsing of the script is successful, it will instantiate all declarations and evaluate the script body, all in one run.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With