ES2026 Features
ECMAScript 2026 is the finalized ECMA-262 17th edition, approved on 2026-06-30. Its seven feature proposals are Array.fromAsync, Error.isError, Math.sumPrecise, Uint8Array base64/hex conversion, Iterator.concat, Map/WeakMap upsert, and JSON source text access.
Runtime ship dates are independent of edition membership. Several ES2026 APIs were available years before publication, while others still require the newest engines or a polyfill.
Contents
- Array.fromAsync
- Error.isError
- Math.sumPrecise
- Uint8Array base64 and hex
- Iterator.concat
- Map and WeakMap upsert
- JSON source text access
- Compatibility
- Sources
Array.fromAsync
Create an array from an async iterable, a sync iterable, or an array-like value. The optional map function may also be async.
async function* fetchPages() {
yield await fetch('/page/1').then(response => response.json());
yield await fetch('/page/2').then(response => response.json());
}
const pages = await Array.fromAsync(fetchPages());
const normalized = await Array.fromAsync(
asyncSource,
async value => normalize(value),
);The source is pulled in order and each value is awaited before the next is requested. This differs from Promise.all, whose input work is normally created up front and can proceed concurrently.
// Concurrent after the map callbacks create all fetch promises.
const responses = await Promise.all(urls.map(url => fetch(url)));
// Sequentially pulls and awaits values yielded by this generator.
const responsesInOrder = await Array.fromAsync(fetchSequentially(urls));Use Array.fromAsync to drain an async source. Do not replace Promise.all with it when concurrency is intentional.
Error.isError
Check the Error internal brand rather than the current realm's prototype chain.
const otherRealmError = iframe.contentWindow.eval('new Error("oops")');
otherRealmError instanceof Error; // false
Error.isError(otherRealmError); // true
Error.isError(new TypeError('bad')); // true
Error.isError({ message: 'fake' }); // false
Error.isError(Object.create(Error.prototype)); // falseThe ECMA-262 operation checks the [[ErrorData]] internal slot, so it is cross-realm-safe for ECMAScript Error instances. Host error objects are a separate integration question: current Safari has partial support because DOMException is not recognized there. Feature or behavior-test that case if the application depends on it.
Math.sumPrecise
Sum an iterable of Number values as an exact mathematical sum of the binary Number inputs, then round once to the nearest representable Number.
const values = [1e20, 0.1, -1e20];
values.reduce((sum, value) => sum + value, 0); // 0
Math.sumPrecise(values); // 0.1
Object.is(Math.sumPrecise([]), -0); // trueIt rejects non-Number values (including BigInt), preserves the specified NaN/infinity behavior, and returns -0 for an empty iterable.
Math.sumPrecise removes intermediate floating-point rounding loss. It does not turn decimal fractions into exact decimal values, so it is not a substitute for integer minor units or a decimal arithmetic design in financial code.
Uint8Array base64 and hex
Convert byte arrays without Latin-1-only atob/btoa loops or Node-specific Buffer code.
const bytes = Uint8Array.fromBase64('SGVsbG8gV29ybGQ=');
const fromHex = Uint8Array.fromHex('48656c6c6f');
bytes.toBase64();
bytes.toBase64({ alphabet: 'base64url', omitPadding: true });
bytes.toHex();
const target = new Uint8Array(1024);
const { read, written } = target.setFromBase64('SGVsbG8=');The complete group is:
Uint8Array.fromBase64/fromHexUint8Array.prototype.toBase64/toHexUint8Array.prototype.setFromBase64/setFromHex
Base64 options control the alphabet and final-chunk handling. Choose strict decoding when accepting security-sensitive or canonical input rather than relying on permissive defaults.
Iterator.concat
Lazily sequence any number of iterables.
const combined = Iterator.concat(
[1, 2],
new Set([3, 4]),
[5, 6],
);
combined.toArray(); // [1, 2, 3, 4, 5, 6]It composes with the ES2025 sync iterator helpers and does not build an intermediate collection:
const firstThreeEven = Iterator.concat(streamA(), streamB())
.filter(value => value % 2 === 0)
.take(3)
.toArray();The result is still single-pass. Do not reuse it as though it were an array.
Map and WeakMap upsert
Read an existing value or insert a default. The same two methods exist on Map.prototype and WeakMap.prototype:
getOrInsert(key, defaultValue)getOrInsertComputed(key, callback)
const byCategory = new Map();
for (const item of items) {
byCategory.getOrInsert(item.category, []).push(item);
}
const cache = new WeakMap();
const metadata = cache.getOrInsertComputed(node, key => inspect(key));Use the computed form when creating the default is expensive. Function arguments are evaluated before the call, so getOrInsert(key, expensive()) performs the work even when the key is already present.
These methods insert only when the key is absent. An existing value of undefined is still an existing value and is returned without replacement.
JSON source text access
The reviver's third context argument exposes the exact source text for primitive values. JSON.rawJSON lets a replacer or value preserve already-validated primitive JSON text during serialization.
const data = JSON.parse(
'{"id": 12345678901234567890}',
(key, value, context) => key === 'id'
? BigInt(context.source)
: value,
);
data.id; // 12345678901234567890n
JSON.stringify({ id: JSON.rawJSON(data.id.toString()) });
// '{"id":12345678901234567890}'
JSON.isRawJSON(JSON.rawJSON('123')); // trueThe context object with source is supplied when reviving primitive values, not objects or arrays. Guard accesses when a reviver handles both:
const parsed = JSON.parse(text, (key, value, context) => {
if (key === 'amount' && context?.source !== undefined) {
return parseExactAmount(context.source);
}
return value;
});JSON.rawJSON validates that its input is suitable primitive JSON text. It is not a way to splice arbitrary object syntax into output.
Compatibility
First unflagged versions from MDN Browser Compatibility Data, checked 2026-08-13:
| Feature | Chrome | Firefox | Safari | Node.js |
|---|---|---|---|---|
Array.fromAsync |
121 | 115 | 16.4 | 22.0.0 |
Error.isError |
134 | 138 | 18.4 partial | 24.3.0 |
Math.sumPrecise |
147 | 137 | 26.2 | No current support recorded |
| Uint8Array base64/hex | 140 | 133 | 18.2 | 25.0.0 |
Iterator.concat |
146 | 147 | 26.4 | 26.0.0 |
| Map/WeakMap upsert | 145 | 144 | 26.2 | 26.0.0 |
| JSON source access/raw JSON | 114 | 135 | 18.4 | 21.0.0 |
Safari 18.4's Error.isError does not recognize DOMException; Node is complete from 24.3. Current core-js has modules for all seven feature groups. Read COMPATIBILITY.md before selecting a polyfill strategy.