ECMAScript 2025: The New JavaScript Features You Need to Know
Hello HaWkers, ECMAScript 2025 has been officially approved and brings a series of features that will make JavaScript developers' lives easier. From regular expression improvements to new array types for graphics and machine learning, this update has something for everyone.
Have you ever wondered how JavaScript keeps evolving year after year? Let's explore each new ES2025 feature with practical examples you can start using today.
Overview of New Features
Before diving into details, here's a summary of what ECMAScript 2025 brings:
Main additions:
- Promise.try() - Safe execution of synchronous functions
- Float16Array - New half-precision TypedArray
- Duplicate Named Capture Groups - More flexible regex
- RegExp.escape() - Automatic special character escaping
- Set methods - Native set operations
- Iterator helpers - Auxiliary methods for iterators
💡 Context: JavaScript has received annual updates since 2015 (ES6), keeping the language modern and competitive.
Promise.try(): Uniform Function Handling
One of the most practical ES2025 features is Promise.try(), which solves a common problem: executing functions that can be synchronous or asynchronous.
The Old Problem
Before, if you had a function that could throw synchronously, you needed special handling:
// Problem: if getUser throws sync error, catch doesn't get it
function fetchUserData(userId) {
// If userId is invalid, this throws BEFORE the Promise
const user = getUser(userId); // May throw sync error!
return Promise.resolve(user)
.then(user => processUser(user))
.catch(err => handleError(err)); // Doesn't catch sync errors!
}
// Old solution: wrap in try-catch or Promise
function fetchUserDataSafe(userId) {
return new Promise((resolve, reject) => {
try {
const user = getUser(userId);
resolve(user);
} catch (err) {
reject(err);
}
}).then(user => processUser(user))
.catch(err => handleError(err));
}The Solution with Promise.try()
Now, everything becomes simpler:
// ES2025: Promise.try solves the problem elegantly
function fetchUserData(userId) {
return Promise.try(() => getUser(userId))
.then(user => processUser(user))
.catch(err => handleError(err));
}
// Works with async functions too
function fetchUserDataAsync(userId) {
return Promise.try(async () => {
const user = await getUser(userId);
const profile = await getProfile(user.id);
return { user, profile };
})
.then(data => processData(data))
.catch(err => handleError(err));
}
// Practical example: validation and fetch
function loadResource(resourceId) {
return Promise.try(() => {
// Sync validation that may throw
if (!isValidId(resourceId)) {
throw new Error('Invalid ID');
}
// Async operation
return fetch(`/api/resources/${resourceId}`);
})
.then(response => response.json())
.catch(err => {
console.error('Error loading resource:', err);
return null;
});
}
Float16Array: Precision for Graphics and ML
The new Float16Array adds support for 16-bit floating point numbers, essential for graphics and machine learning.
Why 16 Bits?
Precision comparison:
| Type | Bits | Memory | Use |
|---|---|---|---|
| Float16 | 16 | 2 bytes | Graphics, ML |
| Float32 | 32 | 4 bytes | General use |
| Float64 | 64 | 8 bytes | High precision |
Using Float16Array
// Creating a Float16Array
const halfFloats = new Float16Array(4);
halfFloats[0] = 1.5;
halfFloats[1] = 2.25;
halfFloats[2] = 0.125;
halfFloats[3] = -3.75;
console.log(halfFloats); // Float16Array(4) [1.5, 2.25, 0.125, -3.75]
// Creating from existing array
const values = [0.5, 1.0, 1.5, 2.0, 2.5];
const f16 = Float16Array.from(values);
// Useful for WebGL and WebGPU
const vertexData = new Float16Array([
// x, y, z, u, v
-1.0, -1.0, 0.0, 0.0, 0.0,
1.0, -1.0, 0.0, 1.0, 0.0,
0.0, 1.0, 0.0, 0.5, 1.0,
]);
// Memory savings for ML
const weights = new Float16Array(1000000); // 2MB vs 4MB with Float32Helper Functions
// New Math functions for Float16
const f16Value = Math.f16round(3.14159); // Rounds to f16 precision
console.log(f16Value); // 3.140625
// Useful for shaders and GPU computing
function prepareGPUData(floatArray) {
// Convert Float32 to Float16 to send to GPU
const f16Data = Float16Array.from(floatArray);
return f16Data.buffer;
}
// DataView also supports Float16
const buffer = new ArrayBuffer(8);
const view = new DataView(buffer);
view.setFloat16(0, 1.5); // Write Float16
view.setFloat16(2, 2.5, true); // Little-endian
const value1 = view.getFloat16(0); // Read Float16
const value2 = view.getFloat16(2, true);
Duplicate Named Capture Groups
A frustrating regex limitation has been solved: you can now use the same group name in different parts of a regular expression.
The Old Problem
// Before: this was a syntax error
// const dateRegex = /(?<day>\d{2})-(?<month>\d{2})|(?<month>\d{2})\/(?<day>\d{2})/;
// SyntaxError: Duplicate capture group name
// Old solution: different names
const dateRegexOld = /(?<day1>\d{2})-(?<month1>\d{2})|(?<month2>\d{2})\/(?<day2>\d{2})/;
const match = '25-12'.match(dateRegexOld);
// Needed to check which group matched
const day = match.groups.day1 || match.groups.day2;
const month = match.groups.month1 || match.groups.month2;The ES2025 Solution
// ES2025: same name in different alternatives
const dateRegex = /(?<day>\d{2})-(?<month>\d{2})|(?<month>\d{2})\/(?<day>\d{2})/;
// Format DD-MM
const match1 = '25-12'.match(dateRegex);
console.log(match1.groups.day); // "25"
console.log(match1.groups.month); // "12"
// Format MM/DD
const match2 = '12/25'.match(dateRegex);
console.log(match2.groups.day); // "25"
console.log(match2.groups.month); // "12"
// More complex example: multiple date formats
const flexibleDate = /
(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})|
(?<day>\d{2})\/(?<month>\d{2})\/(?<year>\d{4})|
(?<month>\d{2})-(?<day>\d{2})-(?<year>\d{4})
/x;
// All work with the same group names
['2025-12-25', '25/12/2025', '12-25-2025'].forEach(date => {
const m = date.match(flexibleDate);
console.log(`${m.groups.year}-${m.groups.month}-${m.groups.day}`);
});
RegExp.escape(): Automatic Escaping
We finally have a native way to escape strings for use in regular expressions.
The Problem
// Before: needed to create own function or use library
function escapeRegex(string) {
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}
// Or risk bugs
const userInput = 'file.txt';
const regex = new RegExp(userInput); // Problem: . matches any character!
'fileAtxt'.match(regex); // Unintended match!The Native Solution
// ES2025: RegExp.escape()
const userInput = 'file.txt';
const escaped = RegExp.escape(userInput);
console.log(escaped); // "file\\.txt"
const regex = new RegExp(escaped);
'file.txt'.match(regex); // Correct match
'fileAtxt'.match(regex); // null - no longer matches
// Useful for literal text search
function findExactText(text, searchTerm) {
const escaped = RegExp.escape(searchTerm);
const regex = new RegExp(escaped, 'gi');
return text.match(regex);
}
// Works with any special character
const specialChars = '(hello) [world] {test} $100 ^start end$';
const safeRegex = new RegExp(RegExp.escape(specialChars));
// Practical example: text highlighting
function highlightText(content, searchTerm) {
const escaped = RegExp.escape(searchTerm);
const regex = new RegExp(`(${escaped})`, 'gi');
return content.replace(regex, '<mark>$1</mark>');
}
highlightText('Price: $99.99', '$99.99');
// "Price: <mark>$99.99</mark>"
Set Methods: Native Set Operations
ES2025 adds native methods for common set operations.
New Methods
const setA = new Set([1, 2, 3, 4, 5]);
const setB = new Set([4, 5, 6, 7, 8]);
// Union: elements in A OR B
const union = setA.union(setB);
console.log([...union]); // [1, 2, 3, 4, 5, 6, 7, 8]
// Intersection: elements in A AND B
const intersection = setA.intersection(setB);
console.log([...intersection]); // [4, 5]
// Difference: elements in A but not in B
const difference = setA.difference(setB);
console.log([...difference]); // [1, 2, 3]
// Symmetric difference: elements in A or B, but not both
const symmetricDiff = setA.symmetricDifference(setB);
console.log([...symmetricDiff]); // [1, 2, 3, 6, 7, 8]
// Subset: is A contained in B?
const small = new Set([4, 5]);
console.log(small.isSubsetOf(setA)); // true
console.log(small.isSubsetOf(setB)); // true
// Superset: does A contain B?
console.log(setA.isSupersetOf(small)); // true
// Disjoint: do A and B have no elements in common?
const setC = new Set([10, 11, 12]);
console.log(setA.isDisjointFrom(setC)); // true
console.log(setA.isDisjointFrom(setB)); // falsePractical Examples
// Permission management
const userPermissions = new Set(['read', 'write', 'delete']);
const requiredPermissions = new Set(['read', 'write']);
const hasAccess = requiredPermissions.isSubsetOf(userPermissions);
console.log(hasAccess); // true
// Find common tags between posts
const post1Tags = new Set(['javascript', 'web', 'frontend']);
const post2Tags = new Set(['javascript', 'nodejs', 'backend']);
const commonTags = post1Tags.intersection(post2Tags);
console.log([...commonTags]); // ['javascript']
// Combine user lists without duplicates
const team1 = new Set(['alice', 'bob', 'charlie']);
const team2 = new Set(['bob', 'diana', 'eve']);
const allMembers = team1.union(team2);
console.log([...allMembers]); // ['alice', 'bob', 'charlie', 'diana', 'eve']
Iterator Helpers: Methods for Iterators
Now iterators have helper methods similar to arrays.
Available Methods
// Creating an iterator
function* infiniteNumbers() {
let n = 0;
while (true) {
yield n++;
}
}
// take(): get N first elements
const first5 = infiniteNumbers().take(5);
console.log([...first5]); // [0, 1, 2, 3, 4]
// drop(): skip N first elements
const skip3take5 = infiniteNumbers().drop(3).take(5);
console.log([...skip3take5]); // [3, 4, 5, 6, 7]
// map(): transform elements
const doubled = infiniteNumbers()
.take(5)
.map(n => n * 2);
console.log([...doubled]); // [0, 2, 4, 6, 8]
// filter(): filter elements
const evens = infiniteNumbers()
.take(10)
.filter(n => n % 2 === 0);
console.log([...evens]); // [0, 2, 4, 6, 8]
// flatMap(): map + flatten
function* pairs(n) {
yield n;
yield n * 10;
}
const flattened = [1, 2, 3].values()
.flatMap(n => pairs(n));
console.log([...flattened]); // [1, 10, 2, 20, 3, 30]Fluent Chaining
// Combining methods
const result = infiniteNumbers()
.drop(10) // Skip first 10
.filter(n => n % 3 === 0) // Only multiples of 3
.map(n => n ** 2) // Square them
.take(5); // Take 5 results
console.log([...result]); // [144, 225, 324, 441, 576]
// Practical example: data processing
function* readLines(text) {
for (const line of text.split('\n')) {
yield line;
}
}
const logData = `
2025-01-01 ERROR: Connection failed
2025-01-01 INFO: Server started
2025-01-02 ERROR: Timeout
2025-01-02 INFO: Request processed
2025-01-03 ERROR: Invalid input
`;
const errors = readLines(logData)
.filter(line => line.includes('ERROR'))
.map(line => line.split('ERROR:')[1]?.trim())
.filter(msg => msg);
console.log([...errors]);
// ['Connection failed', 'Timeout', 'Invalid input']
Compatibility and Support
Before using these features in production, check support:
Implementation Status
| Feature | Chrome | Firefox | Safari | Node.js |
|---|---|---|---|---|
| Promise.try | 128+ | 132+ | 18.2+ | 22+ |
| Float16Array | 128+ | In dev | 18.2+ | 22+ |
| Duplicate Groups | 125+ | 129+ | 18+ | 21+ |
| RegExp.escape | 130+ | 134+ | In dev | 23+ |
| Set methods | 122+ | 127+ | 17+ | 22+ |
| Iterator helpers | 122+ | 131+ | 17+ | 22+ |
Polyfills and Transpilation
// For environments that don't support, use core-js
import 'core-js/actual/promise/try';
import 'core-js/actual/set';
import 'core-js/actual/iterator';
// Or configure babel with preset-env
// babel.config.js
module.exports = {
presets: [
['@babel/preset-env', {
targets: '> 0.25%, not dead',
useBuiltIns: 'usage',
corejs: 3
}]
]
};
Conclusion
ECMAScript 2025 brings practical improvements that solve real everyday problems. Promise.try() simplifies error handling, Float16Array opens doors for graphics and ML, and the new Set and Iterator methods make common operations more elegant.
JavaScript continues to evolve consistently, adding features that previously required external libraries or boilerplate code. Staying updated with these new features is essential for writing cleaner and more efficient code.
If you want to dive deeper into modern JavaScript, I recommend checking out another article: Discovering the Power of Async/Await in JavaScript where you'll discover how to master asynchronous programming.
Let's go! 🦅
💻 Master JavaScript for Real
The knowledge you gained in this article is just the beginning. There are techniques, patterns, and practices that transform beginner developers into sought-after professionals.
Invest in Your Future
I've prepared complete material for you to master JavaScript:
Payment options:
- 1x of $4.90 no interest
- or $4.90 at sight

