Load javascript async, then check DOM loaded before executing callback
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
When a script is loaded asynchronously, it may finish before or after the DOM is ready. If the callback touches page elements, the safe pattern is to wait for both conditions: the external script must be loaded, and the document must have reached a usable ready state.
Wait For The Script And The DOM
There are two separate events involved:
- The script file finishes loading.
- The document is ready for DOM queries and updates.
Treating those as one event is what causes race conditions. A reliable solution is to model each one explicitly, then run the callback only after both promises resolve.
This pattern is robust because it does not assume which event happens first. If the DOM is already ready, domReady() resolves immediately. If the script finishes later, Promise.all still waits for it.
Why async Alone Is Not Enough
The async attribute improves page performance because the browser downloads the script without blocking HTML parsing. The tradeoff is that execution timing becomes independent from DOM parsing order.
That is fine for scripts that do not care about the page structure, but it is risky for code that does something like document.querySelector("#menu"). If that node has not been parsed yet, the callback sees null and fails in a way that looks intermittent.
If you control the markup and just need a static page script, defer is often simpler than async because deferred scripts execute after parsing. But when you are dynamically injecting a script, using a promise-based loader plus a DOM readiness check is the most flexible approach.
Example With A Third-Party SDK
A common real-world use case is loading an SDK only on pages that need it. In that case, the callback should run only after the library exists and the placeholder element has been parsed.
The important detail is that window.AnalyticsWidget.init is called only after the SDK has loaded. Waiting for DOM readiness alone would not guarantee that the global object exists.
Use The Right Ready Signal
For DOM access, DOMContentLoaded is usually the correct event. It fires after the initial HTML has been fully parsed. The later load event waits for images, stylesheets, and other external resources, which often adds unnecessary delay.
Choose load only when the callback truly depends on those extra assets. For most UI initialization, DOMContentLoaded gives the earliest safe point.
Also note that if your code executes after the document is already ready, adding a listener is not enough by itself. That is why the helper checks document.readyState first. Without that check, late calls can stall forever because the event has already passed.
Common Pitfalls
The first pitfall is assuming script.onload means the DOM is ready. It only means the script file finished loading and executing.
Another pitfall is using window.onload for everything. It works, but it delays execution until far later than necessary, which can make widgets feel sluggish.
Some code accidentally calls the callback twice: once from DOMContentLoaded and once from onload. Wrapping both prerequisites in promises avoids duplicate execution.
Error handling is also often missing. If the network request for the script fails, a silent failure can leave the page in a broken state. Rejecting the promise and logging or recovering from the error makes the behavior easier to debug.
Finally, be careful with dependencies between async scripts. If one script expects another global to exist, load them in explicit sequence rather than starting them all at once.
Summary
- Async script loading and DOM readiness are separate conditions.
- A reliable solution waits for both with
Promise.all. - '
DOMContentLoadedis usually the right signal for DOM-safe initialization.' - Check
document.readyStateso late callers still resolve correctly. - Add error handling for failed script loads and avoid duplicate callback paths.
Related reading
- Load jQuery Mobile script asynchronously?
- Load XDocument asynchronously
- loading js files and other dependent js files asynchronously
- Lock-free algorithm library
- Load local html into UIWebView using swift
- Load resources from relative path using local html in uiwebview
- Lock-free multi-threading is for real threading experts
- Lock-free multi-threading is for real threading experts
.png&w=3840&q=75)
Tackling System Design Interview Problems
A short course that equips you with the skills to approach system design interviews methodically.
Start the free courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.