Start · Sprachen · JavaScript · Referenz · import.defer()

import.defer()

Keyword

Lädt ein Modul dynamisch wie <code>import()</code>, verschiebt aber dessen Ausführung, bis zum ersten Mal auf eine Eigenschaft des zurückgegebenen Namensraum-Objekts zugegriffen wird.

seit JavaScript TC39-Proposal „Deferring Module Evaluation“ (Stage Kategorie: syntax

Signatur

import.defer(moduleName, options)

Beschreibung

import.defer() ist die dynamische Variante von import defer * as ns from "…". Wie bei import() wird das angegebene Modul samt Abhängigkeiten asynchron geladen und verknüpft. Der Unterschied: Das Promise wird mit einem deferred module namespace object erfüllt, also einem „aufgeschobenen“ Namensraum-Objekt. Der Code des Moduls läuft zu diesem Zeitpunkt noch nicht.

Erst wenn Sie zum ersten Mal auf einen Export zugreifen (z. B. ns.formatDate), wird das Modul synchron ausgeführt, genau an dieser Stelle im Code. Danach verhält sich das Objekt wie ein normaler Modul-Namensraum. So entfallen Netzwerk-Wartezeiten (geladen ist ja schon alles), und die teils teure Initialisierung eines Moduls (Top-Level-Code, große Tabellen, Polyfills) fällt nur an, wenn sie wirklich gebraucht wird.

Typische Einsatzfälle sind schwere, selten genutzte Funktionen wie Export-Dialoge, Diagramm-Bibliotheken oder Fehlerberichte. Sie sollen frühzeitig vorgeladen, aber nicht gleich beim Start ausgeführt werden. Das verbessert die Startzeit, ohne dass Aufrufer später await brauchen.

Einschränkung: Enthalten das Modul oder eine seiner Abhängigkeiten Top-Level-await, lässt sich dieser asynchrone Teil nicht synchron nachholen. Solche Module werden deshalb bereits vor dem Erfüllen des Promises ausgeführt. Aufgeschoben wird nur der synchrone Teil des Modulgraphen.

Parameter

Name Typ Default Beschreibung
$moduleName Pflicht string Modul-Bezeichner, wie bei import(): ein relativer oder absoluter Pfad, eine URL oder ein Bare Specifier, der über eine Import-Map bzw. die Node.js-Auflösung aufgelöst wird.
$options object undefined Optionales Objekt wie bei import(), z. B. { with: { type: "json" } } für Import-Attribute.

Rückgabewert

Typ
Promise<DeferredModuleNamespace>
Beschreibung
Ein Promise, das mit dem aufgeschobenen Namensraum-Objekt erfüllt wird, sobald das Modul geladen und verknüpft ist (asynchrone Abhängigkeiten sind dann bereits ausgeführt). Das Promise wird abgelehnt, wenn Laden, Parsen oder Verknüpfen fehlschlagen. Laufzeitfehler des aufgeschobenen Moduls treten dagegen erst beim ersten Eigenschaftszugriff auf.

Beispiele

Grundprinzip: Ausführung erst beim Zugriff

// heavy.js
// console.log("heavy.js wird ausgeführt");
// export const answer = 42;

const ns = await import.defer("./heavy.js");
console.log("Modul geladen, aber noch nicht ausgeführt");

// Erster Zugriff löst die synchrone Ausführung aus
console.log(ns.answer);
Modul geladen, aber noch nicht ausgeführt heavy.js wird ausgeführt 42

Schwere Export-Funktion vorladen, bei Klick sofort nutzen

// Beim Start im Hintergrund laden, aber nicht ausführen
const exporterPromise = import.defer("./pdf-exporter.js");

document.querySelector("#export").addEventListener("click", async () => {
  const exporter = await exporterPromise; // meist schon erfüllt
  // Erst hier läuft die teure Initialisierung des Moduls
  const blob = exporter.createPdf(document.body);
  console.log("PDF erzeugt:", blob.size, "Bytes");
});

Laufzeitfehler treten beim Zugriff auf

// broken.js: throw new Error("Init fehlgeschlagen");

const ns = await import.defer("./broken.js"); // kein Fehler hier

try {
  ns.something; // Ausführung startet und wirft
} catch (err) {
  console.error("Fehler beim Auswerten:", err.message);
}
Fehler beim Auswerten: Init fehlgeschlagen

Mit Import-Attributen (JSON-Modul)

const config = await import.defer("./config.json", {
  with: { type: "json" }
});

// Erst jetzt wird das JSON-Modul ausgewertet
console.log(config.default.apiUrl);
https://api.example.com

// Wichtig · Fallstricke

  • Kompatibilität: import.defer() ist ein TC39-Vorschlag in Stufe 3 und bisher in kaum einer Engine standardmäßig aktiv. Einzelne Engines bieten es teils hinter Flags an. TypeScript (ab 5.9) und Babel verstehen die Syntax, Bundler unterstützen sie unterschiedlich gut. Prüfen Sie Ihre Zielumgebung vor dem Einsatz im Produktivcode.
  • Top-Level-await: Asynchrone Module im Graphen werden sofort ausgeführt, nicht aufgeschoben. Die Einsparung fällt dann kleiner aus.
  • Synchrone Ausführung beim Zugriff: Der erste Eigenschaftszugriff kann unerwartet teuer sein oder eine Ausnahme werfen, etwa in einem Event-Handler oder einer Render-Schleife. Setzen Sie den Zugriff bei Bedarf in try/catch.
  • Laut Vorschlag löst der Zugriff auf then (und auf Symbol-Schlüssel wie Symbol.toStringTag) die Ausführung nicht aus. Deshalb kann das Namensraum-Objekt ein Promise erfüllen, ohne vorzeitig ausgewertet zu werden.
  • Wurde dasselbe Modul schon anderswo normal importiert und ausgeführt, gibt es nichts mehr aufzuschieben. Modulinstanzen werden wie üblich geteilt.
  • Laden und Parsen passieren trotzdem vorab. Netzwerk- oder Syntaxfehler lehnen deshalb schon das Promise ab.