Web Development · JavaScript
ECMAScript 2026: What You Get Beyond the Temporal API
TC39 finalized ECMAScript 2026 with eight proposals at the March 2026 plenary. Temporal gets the headlines, but the rest of the spec (Iterator Helpers, new Set methods, Promise.try, RegExp.escape, and Float16Array) is worth understanding too. Here's what ships and when you can use it.
Abhishek Gupta
8 min read
Sponsored
Temporal is the marquee name in ECMAScript 2026, a complete replacement for the JavaScript Date object that has been broken since 1995. If you haven’t looked at Temporal yet, the Temporal post covers what it does and how to use it.
This post is about everything else TC39 finalized in the March 2026 plenary. Eight proposals in total reached Stage 4. The remaining seven are smaller than Temporal individually, but several of them will show up in your code far more often.
Iterator Helpers
The iterator protocol has been in JavaScript since ES6. for...of loops, spread syntax, and destructuring all depend on it. Generators produce iterators. Map, Set, String, and arrays are all iterable. What iterators lacked until now was the functional toolkit that makes working with them ergonomic.
Array methods like .map() and .filter() work on arrays only. They always produce a new array, even if you only want the first 10 results from a filtered list. Iterator Helpers add the same operations directly to the iterator protocol, but lazy: values are computed on demand rather than materializing an intermediate collection.
// Before: filtering a large array, taking the first few
const result = users
.filter(u => u.active)
.map(u => u.email)
.slice(0, 10);
// Creates two full intermediate arrays before slice
// With Iterator Helpers: lazy, no intermediate arrays
const result = users
.values() // get an iterator from the array
.filter(u => u.active)
.map(u => u.email)
.take(10)
.toArray(); // only materializes 10 values
The helpers available on any iterator:
| Method | Description |
|---|---|
.map(fn) | Transforms each value |
.filter(fn) | Keeps values where fn returns true |
.take(n) | Yields at most n values, then stops |
.drop(n) | Skips the first n values |
.flatMap(fn) | Maps then flattens one level |
.reduce(fn, init) | Folds values into a single result |
.forEach(fn) | Runs fn for each value (no return) |
.some(fn) | Returns true if any value matches |
.every(fn) | Returns true if all values match |
.find(fn) | Returns the first matching value |
.toArray() | Materializes the iterator into an array |
Because these work on the protocol itself, they compose with any iterable — including generators:
function* integers() {
let n = 0;
while (true) yield n++;
}
const firstFiftyEvenSquares = integers()
.filter(n => n % 2 === 0)
.map(n => n * n)
.take(50)
.toArray();
// Works on an infinite generator. Only 50 values ever computed.
This is available in Node.js v22+ and modern browsers without any import or polyfill.
New Set methods
JavaScript’s Set has been useful for deduplication but not much else. If you wanted the union of two sets, you had to write it yourself. ECMAScript 2026 adds seven new methods:
const a = new Set([1, 2, 3, 4]);
const b = new Set([3, 4, 5, 6]);
a.union(b); // Set {1, 2, 3, 4, 5, 6}
a.intersection(b); // Set {3, 4}
a.difference(b); // Set {1, 2} (in a, not in b)
a.symmetricDifference(b); // Set {1, 2, 5, 6} (in either, not both)
a.isSubsetOf(b); // false
a.isSupersetOf(b); // false
a.isDisjointFrom(b); // false (they share 3 and 4)
new Set([3, 4]).isSubsetOf(a); // true
These methods accept any iterable, not just other Sets:
const activeUserIds = new Set([1, 2, 3]);
const premiumUserIds = [2, 3, 4, 5]; // an array works
activeUserIds.intersection(premiumUserIds); // Set {2, 3}
The practical benefit: tag filtering, permission set operations, feature flag intersection, and diff computations all become a single method call instead of a manual loop.
Promise.try()
Every async function or promise chain has the same edge case: synchronous code that runs before the first await. If that code throws, the thrown error is not captured by the promise chain. It becomes an uncaught exception.
async function loadData(id) {
validateId(id); // throws synchronously if id is invalid
const data = await fetch(`/api/data/${id}`).then(r => r.json());
return data;
}
loadData("bad!").catch(err => console.error("caught:", err));
// Works fine — async function wraps everything in a Promise
// But what about a non-async wrapper?
function loadAndProcess(id) {
validateId(id); // if this throws, the caller must catch it *synchronously*
return fetch(`/api/data/${id}`)
.then(r => r.json())
.then(process);
}
loadAndProcess("bad!")
.catch(err => console.error("caught:", err));
// The .catch does NOT catch a synchronous throw from validateId
Promise.try() closes this gap:
function loadAndProcess(id) {
return Promise.try(() => {
validateId(id); // if this throws synchronously...
return fetch(`/api/data/${id}`)
.then(r => r.json())
.then(process);
});
// ...Promise.try catches it and converts it to a rejection
}
loadAndProcess("bad!")
.catch(err => console.error("caught:", err)); // works correctly now
It also handles the reverse: a function that sometimes returns a value and sometimes returns a promise:
Promise.try(() => maybeAsync(input)).then(result => ...);
// Works whether maybeAsync returns a value or a Promise
Before Promise.try, the idiomatic workaround was Promise.resolve().then(() => syncFn()), which is less legible and easy to forget. Promise.try is the clearer version now standardized.
RegExp.escape()
Regular expressions use several characters as metacharacters: ., *, +, ?, (, ), [, ], {, }, ^, $, |, \. If you build a regex from user input, any of these characters will be interpreted as regex syntax rather than literal text.
The workaround has always been a manual escape function, and nearly every project has a slightly different version:
// Manual escaping (varies by project, often subtly wrong)
function escapeRegex(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
ECMAScript 2026 adds RegExp.escape():
const userQuery = "2.5 (beta)";
const pattern = new RegExp(RegExp.escape(userQuery), "i");
// pattern matches the literal string "2.5 (beta)", not "2" then any char then "5 (beta)"
This is a small but consistently useful addition. You can stop copying the escape function between projects.
Float16Array and DataView 16-bit helpers
If you work with WebGL, WebGPU, neural network weights, or sensor data, you may already know that 16-bit floating point (half-precision) is commonly used for storage and memory efficiency. Until ECMAScript 2026, JavaScript had no native Float16Array. You had to use Uint16Array and convert manually.
ES2026 adds:
// Float16Array: typed array for 16-bit floats
const weights = new Float16Array(1024);
weights[0] = 0.5;
weights[1] = 1.0;
// DataView helpers for reading/writing 16-bit floats in binary buffers
const buf = new ArrayBuffer(4);
const view = new DataView(buf);
view.setFloat16(0, 1.5, true); // little-endian
const val = view.getFloat16(0, true); // 1.5
Math.f16round() is also added, the same idea as Math.fround() but for 16-bit precision:
Math.f16round(1.337); // returns the nearest float16 value
The use case is narrow but the audience is large enough: anyone working with ML inference, 3D graphics pipelines, or sensor data formats where float16 is the on-disk representation benefits from not converting through Uint16.
Import text
This one is Stage 3 at time of writing rather than finalized in ES2026, but it is shipping in Node.js v22+ and Chrome and is close enough to mention.
import text lets you import a text file as a module string:
import promptTemplate from "./prompt.txt" with { type: "text" };
// promptTemplate is the file contents as a string
const filled = promptTemplate.replace("{{user}}", currentUser.name);
Before this, loading a text file in Node.js meant using fs.readFileSync or an import.meta.url trick. With import text, you can include text files as part of the module graph, with the same caching and bundler optimization that apply to JS imports.
What you can use today
Most of these features are available now in current engines:
| Feature | Node.js | Chrome | Firefox | Safari |
|---|---|---|---|---|
| Iterator Helpers | v22+ | 122+ | 131+ | 18+ |
| New Set methods | v22+ | 122+ | 127+ | 17.4+ |
| Promise.try | v24+ | 124+ | 132+ | 18+ |
| RegExp.escape | v24+ | 127+ | 134+ | 18.2+ |
| Float16Array | v20+ | 108+ | 93+ | 15.2+ |
| Temporal | v26+ | 127+ | 139+ | TP |
For projects that need to support older environments, Temporal has a polyfill (@js-temporal/polyfill). Iterator Helpers have a polyfill via core-js. The others are narrower enough that manual polyfilling is usually unnecessary if you’re not targeting legacy environments.
TypeScript 5.8+ has type definitions for all of ES2026. Update your tsconfig.json to "lib": ["ES2026"] or "ES2026.Full" to get the new types in your IDE:
{
"compilerOptions": {
"lib": ["ES2026"],
"target": "ES2026"
}
}
ECMAScript 2026 is a practical release. It does not redefine the language the way async/await or modules did. It fills gaps that have caused real friction: iterator composition without arrays, set operations without manual loops, safe promise wrapping, escaped regex from user input. These land in everyday code, not just specialized use cases.
For teams building JavaScript APIs and services, the security angle intersects here too. RegExp.escape in particular matters whenever you build regex from user input, which is a classic injection vector. For more on keeping Node.js deployments secure, the Node.js June 2026 security release from this week is the practical companion to this post.
Frequently asked questions
- What is ECMAScript 2026?
- ECMAScript is the formal specification JavaScript is built on. TC39, the committee that maintains it, publishes a new edition each year. ECMAScript 2026 was finalized at TC39's March 2026 plenary after multiple proposals reached Stage 4 (the final standardization step). The features from ES2026 are already available in most modern JavaScript engines. They're not future features; they're in the language now.
- Does ECMAScript 2026 include Temporal?
- Yes, Temporal reached Stage 4 in March 2026 and is part of ECMAScript 2026. However, Temporal is a large API that deserves its own coverage. See the companion post on the Temporal API for a full walkthrough. This post covers the other features in the same release.
- What are Iterator Helpers and how do they differ from array methods?
- Iterator Helpers add functional methods (map, filter, take, flatMap, reduce, forEach, some, every, find) to the iterator protocol. Unlike array methods, they are lazy: values are computed on demand rather than materializing a full intermediate array. You can call .map().filter().take(10) on an iterator representing an infinite sequence, and only 10 values are ever computed. This is a meaningful difference for large datasets and generators.
- Are the new Set methods available in Node.js and browsers?
- Yes. The Set methods (union, intersection, difference, symmetricDifference, isSubsetOf, isSupersetOf, isDisjointFrom) are available in Node.js v22+, Chrome 122+, Firefox 127+, and Safari 17.4+. They're available now without a polyfill for any modern target.
- What problem does Promise.try() solve?
- Promise.try(fn) executes fn and wraps the result in a promise. If fn throws synchronously, the error becomes a rejection (not an uncaught exception). If fn returns a non-promise value, it becomes a resolved promise. If fn returns a promise, it's passed through. This gives you a single pattern for bridging sync and async code safely, without a try/catch wrapper around every function entry point.
Sources
Sponsored
More from this category
More from Web Development
R.01 MySQL Now Has a Native VECTOR Type. Should You Drop pgvector For It?
R.02 React 19.2 vs 'React 20': There Is No React 20, and Here's What's Actually New
R.03 Next.js's First Scheduled Security Release Shipped: 9 Advisories, What to Patch
Sponsored
Discussion
Join the conversation.
Comments are powered by GitHub Discussions. Sign in with your GitHub account to leave a comment.
Sponsored