v156 · javascript · modules
Deferred imports evaluation ("import defer")
A new import form — import defer * as ns from "./mod.js" — loads and parses a module up front but does not run its top-level code until you first touch a property of the namespace. It is the synchronous, no-await answer to "I want this module ready, but I don't want to pay for running it unless I actually use it."
concepts
-
Syntax and timing
The canonical use, measured. A deferred module is imported, its namespace held untouched, and a shared evaluation log proves its top-level code has not run yet. Then one property access triggers evaluation — with
performance.now()timestamps and the module's own side-effect log as the witness. Grammar support is detected by really trying to import the syntax, not by a version check. -
Eager vs deferred
The same expensive module imported two ways — a plain
import * asand animport defer * as— side by side, with the evaluation timeline for each. Eager runs its cost at import; deferred runs nothing until first use, and never if you never use it. The practical case: a rarely-opened feature whose init cost you want off the startup path without rewriting every caller to beasync. -
Feature detection & fallback
What a well-behaved app does when the grammar is not available. A real parse-time probe reports whether this browser understands
import defer; when it does not, the page shows the exact behaviour you would see, the flag to enable it, and a clearly-labelled dynamic-import()fallback that approximates deferral — never a faked success.
why it shipped
Large apps spend real CPU just running module top-level code at startup, even for features the user may never open. The existing tool for deferring that work is dynamic import(), but it forces the whole call chain to become async and usually needs a separate preload step to avoid a request waterfall. import defer targets exactly the execution cost: the module and its synchronous dependency subtree are fully fetched and parsed during normal graph loading, so they are execution-ready, but their top-level evaluation is deferred to the first property access on the namespace — synchronously, with no API change for callers.
One subtlety from the TC39 proposal: property access must be synchronous, so a deferred subtree that contains top-level await cannot be deferred. Those async modules (and their transitive dependencies) are evaluated eagerly during loading, and only the synchronous remainder is left for later. The syntax follows the source-phase import model and only works with namespace imports — there is no deferred named import.
the API
import defer * as inspector from "./heavy-inspector.js";
// Nothing in heavy-inspector.js has run yet — it is loaded and parsed,
// but execution-ready, not executed.
button.addEventListener("click", () => {
// The FIRST property access runs the module's top-level code, synchronously.
inspector.open(currentSelection);
});
// The namespace reports itself as a deferred module until evaluated:
inspector[Symbol.toStringTag]; // "Deferred Module"
research notes
Grounded in the TC39 Deferring Module Evaluation proposal (Stage 3; the [[Get]] on the namespace exotic object is what initiates a synchronous top-level execution, and the namespace's Symbol.toStringTag is "Deferred Module"), plus the note that top-level-await subtrees are eagerly evaluated. Firefox is positive and Safari is shipping/shipped. Runtime flag confirmed in V8's feature-flags.h: js_defer_import_eval ("defer import eval"), a staged feature enabled via --js-flags=--js-defer-import-eval (or --js-flags=--harmony). No chrome://flags entry and no Blink runtime_enabled_features.json5 name — it is a V8-side JavaScript feature. On this project's Chromium 141 baseline the grammar does not parse (SyntaxError: Unexpected token '*'), which the pages detect and degrade around honestly. Portfolio: basic/canonical + inspection timing (syntax-and-timing), practical comparison (eager-vs-deferred), edge/migration/unsupported (feature-detection & fallback). No separate "advanced composition" page — the honest advanced angle (TLA-subtree eager evaluation) cannot be exercised without the grammar on this baseline and is documented instead of faked.