12k
All articles

Was ist neu in ECMAScript 2026

ES2026 bringt sieben JavaScript-Funktionen: Array.fromAsync, Math.sumPrecise, base64- und Hex-Methoden für Uint8Array, JSON.parse Source Access und getOrInsert.

OpenReplay Team
OpenReplay Team
Was ist neu in ECMAScript 2026

Ecma International hat die Sprachspezifikation ECMAScript 2026 am 30. Juni 2026 verabschiedet. Sie enthält genau sieben neue Features: Array.fromAsync, Error.isError, Math.sumPrecise, Base64- und Hex-Methoden für Uint8Array, Iterator.concat, Zugriff auf den Quelltext bei JSON.parse sowie getOrInsert für Map und WeakMap.

Jedes Jahr landen diese Methoden ganz unauffällig in der Sprache, und die meisten von uns begegnen ihnen erst an dem Tag, an dem wir einen Helper löschen, den wir jahrelang mit uns herumgetragen haben. Diesmal ist es vermutlich ein Base64-Utility.

Wer in letzter Zeit nach ES2026 gesucht hat, ist wahrscheinlich auf Listen gestoßen, die einander widersprechen – etliche davon führen Temporal oder das using-Keyword auf. Diese Listen sind falsch. Dieser Artikel erklärt, warum, und geht anschließend jedes der sieben tatsächlich ausgelieferten Features durch: das Problem, das es löst, den Code, den es ersetzt, und den Einzeiler, der an dessen Stelle tritt.

Maßgeblich sind die Freigabemeldung von Ecma, die ES2026 als 17. Ausgabe von ECMA-262 bestätigt, sowie die Tabelle der abgeschlossenen Proposals von TC39, in der genau sieben Zeilen als Expected Publication Year das Jahr 2026 tragen.

Die wichtigsten Erkenntnisse

  • ES2026 wurde am 30. Juni 2026 verabschiedet und ergänzt genau sieben Features; Temporal und Explicit Resource Management gehören nicht dazu.
  • Math.sumPrecise([1e17, 1, -1e17]) liefert 1, während dieselben Werte mit reduce summiert 0 ergeben.
  • Uint8Array erhält die eingebauten Methoden toBase64(), toHex(), fromBase64() und fromHex() und macht damit eine verbreitete Abhängigkeit überflüssig.
  • Der Reviver von JSON.parse erhält jetzt ein drittes Argument, das den rohen Quelltext bereitstellt, und JSON.rawJSON() löst das Problem auf der Stringify-Seite – BigInts überstehen damit einen JSON-Roundtrip.
  • Map.prototype.getOrInsert(key, value) gibt den vorhandenen Wert zurück, wenn der Schlüssel bereits existiert; der Standardwert wird nur eingefügt, wenn der Schlüssel fehlt.

Was hat es nicht in ES2026 geschafft?

Temporal und Explicit Resource Management (using / await using) sind nicht Teil von ES2026. In der Liste der abgeschlossenen TC39-Proposals tragen beide als Expected Publication Year das Jahr 2027 – ebenso wie Atomics.pause und Joint Iteration. Alle vier haben Stage 4, die letzte Stufe des TC39-Prozesses, erst nach dem Stichtag für ES2026 erreicht. Berichte aus der Zeit vor der Verabschiedung, Ende 2025, sagten vielfach voraus, sie würden 2026 landen – daher stammen die widersprüchlichen Feature-Listen. Die Prüfung kann jeder selbst durchführen: einfach die Spalte Expected Publication Year in der Tabelle der abgeschlossenen Proposals filtern. Sieben Proposals stehen dort auf 2026, und keines davon ist Temporal.

Array.fromAsync

Array.fromAsync nimmt einen asynchronen Iterator entgegen und gibt ein Array zurück, verpackt in ein Promise. Es akzeptiert außerdem einen synchronen Iterator, dessen Werte Promises sind, ein array-ähnliches Objekt sowie optional eine Mapping-Funktion – genau wie Array.from. Bisher bedeutete das Einsammeln eines asynchronen Iterators eine for await-Schleife, die in ein veränderliches Array pusht:

const arr = [];
for await (const n of asyncGen(1, 3)) {
  arr.push(`page ${n}`);
}

Dasselbe Ergebnis ist jetzt ein einziger Ausdruck:

const arr = await Array.fromAsync(asyncGen(1, 3), (n) => `page ${n}`);
// [ 'page 1', 'page 2', 'page 3' ]

Error.isError

Error.isError(value) gibt nur dann true zurück, wenn der abgefangene Wert ein echtes Error-Objekt ist. JavaScript erlaubt es, beliebige Werte zu werfen, und instanceof Error führt in die Irre, sobald Werte Grenzen zwischen iframes oder Realms überschreiten, denn jeder Realm hat seinen eigenen Error-Konstruktor. Ein Catch-Block, der an instanceof Error hängt, kann stillschweigend genau den Zweig überspringen, der eine nützliche Meldung rendert – und Session Replays dieser Fehlerklasse zeigen ein vertrautes Bild: Der Nutzer hängt auf einem Screen fest, der nichts meldet, während darunter ein echter Throw stattgefunden hat. Error.isError schließt diese Lücke.

try {
  throw new Error("session upload failed");
} catch (error) {
  Error.isError(error); // true
}

try {
  throw "session upload failed";
} catch (error) {
  Error.isError(error); // false
}

Math.sumPrecise

Math.sumPrecise summiert ein Iterable von Zahlen, ohne dass sich in den Zwischenergebnissen Fließkommafehler ansammeln. Eine reduce-Kette addiert paarweise, sodass sich große und kleine Größenordnungen ungünstig auslöschen:

const values = [1e17, 1, -1e17];

values.reduce((a, b) => a + b, 0); // 0
Math.sumPrecise(values);           // 1

Die reduce-Variante verliert die 1 vollständig. Über die Präzision hinaus verabschiedet sich damit auch der Summen-Reducer als Boilerplate.

Uint8Array toBase64, toHex, fromBase64 und fromHex

ES2026 stattet Uint8Array mit den eingebauten Methoden toBase64() und toHex() aus, dazu kommen die statischen Methoden Uint8Array.fromBase64() und Uint8Array.fromHex() zum Dekodieren. Die Umwandlung von Binärdaten in Text bedeutete bislang, eine Bibliothek einzubinden oder die Kodierung selbst zu schreiben; das ist eine Abhängigkeit, die die meisten Codebases jetzt löschen können.

const value = new Uint8Array([79, 112, 101, 110, 82, 101, 112, 108, 97, 121]);

const valueBase64 = value.toBase64(); // 'T3BlblJlcGxheQ=='
const decoded = Uint8Array.fromBase64(valueBase64);

new TextDecoder().decode(decoded); // 'OpenReplay'

Iterator.concat

Iterator.concat() verkettet Iteratoren der Reihe nach. Bisher ging das nur über einen handgeschriebenen Generator, der jede Quelle durchläuft und mit yield* weiterreicht:

function* combine(...iterators) {
  for (const source of iterators) {
    yield* source;
  }
}

Jetzt ist es ein einziger Aufruf, und einfache Wert-Arrays lassen sich zwischen die Iteratoren einschieben. Das [300] in der Mitte des folgenden Beispiels ist ein gewöhnliches Array und muss nicht in Iterator.from verpackt werden.

const iOne = Iterator.from([100, 200]);
const iTwo = Iterator.from([400, 500]);

const combined = Iterator.concat(iOne, [300], iTwo);
Array.from(combined); // [ 100, 200, 300, 400, 500 ]

Zugriff auf den Quelltext bei JSON.parse

JSON-Roundtrips waren in JavaScript vor ES2026 in beide Richtungen verlustbehaftet. Das Parsen eines großen Integer-Literals rundet es stillschweigend, und das Stringifizieren eines BigInt wirft einen Fehler:

JSON.parse("9007199254740993");   // 9007199254740992
JSON.stringify(9007199254740993n); // TypeError: Do not know how to serialize a BigInt

Das Proposal für den Zugriff auf den Quelltext bei JSON.parse behebt beide Seiten. JSON.parse übergibt seinem Reviver jetzt ein drittes Argument, das den unveränderten source-Text enthält und den man selbst konvertieren kann, ohne Stellen zu verlieren. Auf der Stringify-Seite erlaubt JSON.rawJSON() einem Replacer, einen Wert wortwörtlich auszugeben:

JSON.parse("9007199254740993", (key, value, { source }) => BigInt(source));
// 9007199254740993n

JSON.stringify(9007199254740993n, (key, value) => JSON.rawJSON(value));
// 9007199254740993

Ein BigInt übersteht damit einen JSON-Roundtrip, ohne Stellen zu verlieren oder einen Fehler zu werfen.

Map.prototype.getOrInsert und WeakMap.prototype.getOrInsert

Das Upsert-Proposal bringt getOrInsert(key, value) sowohl für Map als auch für WeakMap, sodass man nicht mehr erst has() und dann set() aufrufen muss. Die Semantik ist entscheidend: getOrInsert gibt den vorhandenen Wert zurück, wenn der Schlüssel bereits existiert. Nur wenn der Schlüssel fehlt, wird eingefügt und der Standardwert zurückgegeben.

const limits = new Map();
limits.set("uploads", 25);

limits.getOrInsert("exports", 10);   // 10  (eingefügt)
limits.getOrInsert("uploads", 100);  // 25  (vorhandener Wert gewinnt)

Beachte, dass 100 verworfen wird, weil "uploads" bereits gesetzt war. Behandle das zweite Argument als Standardwert, nicht als Überschreibung.

Was kannst du jetzt löschen?

ES2026 ist ein Aufräum-Release: Jedes der sieben Features verabschiedet ein Muster, das du heute vermutlich in Produktion hast. Die Base64-Helper-Abhängigkeit, der Summen-Reducer, die has()-dann-set()-Guard-Blöcke, der yield*-Verkettungsgenerator und die for await-Sammelschleife haben alle direkte eingebaute Entsprechungen. Die Verfügbarkeit zur Laufzeit unterscheidet sich je nach Engine – prüfe daher in MDNs Referenz zu den Standard-Built-in-Objekten, wie der aktuelle Support jeder Methode aussieht, bevor du Polyfills entfernst. Fang dann mit dem einfachsten Gewinn in deiner Codebase an – für die meisten Teams ist das der Austausch der Map-Existenzprüfungen gegen getOrInsert.

FAQs

Was ist der Unterschied zwischen Array.fromAsync und Promise.all?

Array.fromAsync wartet Werte sequenziell ab, einen nach dem anderen, und akzeptiert asynchrone Iterables, synchrone Iterables, die Promises liefern, sowie array-ähnliche Objekte. Promise.all führt ein Iterable von Promises nebenläufig aus und rejectet, sobald eines der Promises rejectet. Nutze Promise.all für parallele Arbeit und Array.fromAsync, wenn die Reihenfolge zählt oder die Quelle ein asynchroner Iterator ist. Beide geben ein Promise zurück, das sich zu einem Array auflöst.

Gibt Error.isError bei TypeError und anderen Error-Subklassen true zurück?

Ja. Error.isError gibt für jede echte Error-Subklasse true zurück, einschließlich TypeError, RangeError, SyntaxError und eigener Klassen, die von Error erben, weil Engines eine interne Kennzeichnung prüfen, die echte Fehler tragen, statt die Prototypenkette auszuwerten. In Browsern zählt auch eine DOMException als echter Fehler. Für Objekte, die Fehler lediglich nachahmen – etwa einfache Objekte mit einer message-Eigenschaft oder mit Object.create(Error.prototype) erzeugte Objekte –, gibt die Methode false zurück.

Kann Uint8Array.prototype.toBase64 URL-sicheres Base64 erzeugen?

Ja. toBase64 akzeptiert ein Optionsobjekt mit einer alphabet-Eigenschaft, die auf 'base64' (Standard, mit + und /) oder 'base64url' (mit - und _) gesetzt werden kann, sowie ein boolesches omitPadding, das nachgestellte =-Zeichen weglässt. Uint8Array.fromBase64 akzeptiert dieselbe alphabet-Option sowie eine lastChunkHandling-Option ('loose', 'strict' oder 'stop-before-partial'), die steuert, wie ein abschließender Chunk mit weniger als vier Zeichen dekodiert wird.

Wie vermeide ich mit getOrInsert die Berechnung eines aufwendigen Standardwerts?

Verwende getOrInsertComputed(key, callback), das dasselbe Upsert-Proposal sowohl für Map als auch für WeakMap ergänzt. Der Callback wird nur aufgerufen, um den Wert zu erzeugen, wenn der Schlüssel fehlt; anschließend wird das Ergebnis eingefügt und zurückgegeben. Existiert der Schlüssel bereits, läuft der Callback nie und der vorhandene Wert wird zurückgegeben. Das einfache getOrInsert wertet sein zweites Argument dagegen immer vor dem Aufruf aus, auch wenn der Schlüssel schon vorhanden ist.

Open-source session replay

Complete picture for complete understanding

Capture every clue your frontend is leaving so you can instantly get to the root cause of any issue with OpenReplay — the open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data.

Star on GitHub12k

We use cookies to improve your experience. By using our site, you accept cookies.