
ES2026 was finalized in March. Temporal got the headlines — and it deserved them; replacing Date was long overdue. But six other features landed in the same spec, and most of them will touch more of your code more often than date handling ever will. They don’t have catchy migration guides, but they will quietly change the way you write JavaScript. Here’s the part of ES2026 nobody wrote about.
Explicit Resource Management: The Feature That Changes Async Code
Every developer who has written database queries, opened file handles, or worked with readable streams has also written the same try/finally block dozens of times. Sometimes it gets skipped under deadline pressure. Sometimes the error path forgets the cleanup call. Python has with. C# has using. Java has try-with-resources. JavaScript now has both.
The new using and await using declarations bind a value whose cleanup runs automatically when the scope exits — on success, on error, and on early return.
// Before: manual try/finally on every DB call
async function getUser(id) {
const conn = await db.connect();
try {
return await conn.query('SELECT * FROM users WHERE id = $1', [id]);
} finally {
await conn.close(); // easy to forget; easy to duplicate
}
}
// After: guaranteed cleanup, half the code
async function getUser(id) {
await using conn = await db.connect();
return await conn.query('SELECT * FROM users WHERE id = $1', [id]);
}
The contract is simple: any object that implements Symbol.asyncDispose (or Symbol.dispose for synchronous resources) works with await using. TypeScript 5.2 added syntax support; TypeScript 6.0 ships complete type definitions. Most major ORMs are adding dispose support now. The V8 team’s explainer covers the full DisposableStack API for managing multiple resources in one block.
Browser support is Chrome 134+ and Firefox 134+; Safari support is still incoming. Until then, TypeScript’s transpiler covers you.
Map.getOrInsert: The Method You’ve Been Writing by Hand
There is a pattern every JavaScript developer writes from memory. It looks like this:
if (!map.has(key)) {
map.set(key, []);
}
map.get(key).push(item);
Three lines. A lookup, a check, another lookup. ES2026 collapses it to one method:
map.getOrInsert(key, []).push(item);
The getOrInsertComputed variant is smarter: it only runs the factory function if the key is absent, making it safe for expensive operations like network calls or heavy object construction:
const profile = cache.getOrInsertComputed(userId, () => fetchProfile(userId));
Both methods also land on WeakMap. Browser support is at 77% globally as of May 2026, covering all major engines.
Math.sumPrecise: The Floating-Point Fix That Should Have Arrived in 1995
Floating-point accumulation errors are one of those bugs that ships to production constantly and gets filed under “computers are weird.” The canonical example is harmless-looking:
// The classic
[0.1, 0.2, 0.3].reduce((a, b) => a + b) // 0.6000000000000001
// The one that actually bites you in production
[1e20, 0.1, -1e20].reduce((a, b) => a + b, 0) // Returns 0. Should return 0.1.
The second example is not academic. It’s the kind of bug that appears in financial totals, analytics aggregations, and cart calculations — and only shows up when the numbers get large. Math.sumPrecise uses compensated summation to eliminate the accumulation error entirely:
Math.sumPrecise([1e20, 0.1, -1e20]) // 0.1 — correct
Browser support is currently at 67%, so a polyfill is advisable for production use today. The TC39 finished proposals page links to the spec text and reference implementations.
Four More Worth Knowing
Array.fromAsync does for async iterables what Array.from does for synchronous ones. Collecting paginated API results, draining a message queue, or converting an async generator into an array is now one awaited call instead of a for await...of loop with a results accumulator. It’s baseline across all major browsers.
const items = await Array.fromAsync(fetchPages('/api/items'), page => page.data);
Error.isError is a small fix to a real problem. instanceof Error returns false for errors that cross realm boundaries — errors thrown inside iframes, workers, or vm.runInNewContext. Error.isError(e) handles all of them correctly and is immune to spoofing via Symbol.toStringTag.
RegExp.escape should be treated as a security feature. If you’re constructing regexes from user input without escaping, you’re vulnerable to ReDoS attacks — crafted strings that cause catastrophic backtracking. RegExp.escape(userInput) is now the canonical answer to that class of bug.
Uint8Array base64 and hex encoding replaces btoa()/atob(), which have been broken since the beginning (no URL-safe support, no binary-safe encoding). bytes.toBase64(), bytes.toHex(), and their static counterparts are the replacements. Use { alphabet: 'base64url' } for JWT and URL-safe contexts.
When to Ship This
The short answer: TypeScript 5.2+ for using/await using via transpilation, and native browser support for everything else once you’re comfortable with the compatibility numbers.
- Safe to use now with polyfill: Math.sumPrecise (67% native), using/await using (via TypeScript transpilation)
- Safe for most production apps: Array.fromAsync, Map.getOrInsert, Error.isError, RegExp.escape (77–84% native)
- Broadly available: Uint8Array base64/hex (Chrome 128+, Safari 18+, Firefox 128+)
These aren’t paradigm shifts. ES2026 is the platform admitting it should have shipped these a decade ago. The good news is they’re here now, they’re standard, and they eliminate entire categories of bugs. The spec is final. Check the MDN documentation for await using to start. There’s no reason to keep writing the boilerplate version.













