JavaScript ES2024 Features You Should Know
Explore the most impactful ES2024 features including Array grouping, Promise.withResolvers, well-formed Unicode strings, and the RegExp v flag with practical examples.

JavaScript keeps evolving, and ES2024 (ECMAScript 2024) brings several features that solve real problems I encounter in day-to-day frontend development. Here’s a practical look at the ones worth adopting now.
ES2024 IN ONE GLANCE
The most useful parts of ES2024 are not abstract spec trivia. They remove recurring boilerplate and long-standing edge-case pain.
DATA SHAPING
Object.groupBy and Map.groupBy
Native grouping replaces one of the most repeated `reduce` utilities in app code.
- Cleaner collection transforms
- Less custom utility code
ASYNC CONTROL
Promise.withResolvers
The deferred-promise pattern becomes explicit and much less awkward.
- Better event-driven coordination
- Cleaner timeout wrappers
TEXT SAFETY
Well-formed Unicode strings
These methods catch malformed text before it blows up encoding and data pipelines.
- Safer `encodeURIComponent` usage
- Better handling of user-generated input
REGEX
The `v` flag upgrades Unicode matching
Set operations and better Unicode semantics make regex work more expressive for global products.
- Set subtraction
- Set intersection
- Better emoji and script handling
LOW-LEVEL
ArrayBuffer transfer and Atomics.waitAsync
These matter more for workers, performance-sensitive apps, and systems-style JS than for everyday component code.
- Better ownership transfer
- Non-blocking shared-memory coordination
Object.groupBy and Map.groupBy
Grouping arrays by a property has been a common utility function in every project I’ve worked on. ES2024 makes it native.
Object.groupBy
const products = [
{ name: 'Laptop', category: 'electronics', price: 999 },
{ name: 'Shirt', category: 'clothing', price: 29 },
{ name: 'Phone', category: 'electronics', price: 699 },
{ name: 'Jeans', category: 'clothing', price: 59 },
{ name: 'Tablet', category: 'electronics', price: 449 },
];
const grouped = Object.groupBy(products, (product) => product.category);
// Result:
// {
// electronics: [{ name: 'Laptop', ... }, { name: 'Phone', ... }, { name: 'Tablet', ... }],
// clothing: [{ name: 'Shirt', ... }, { name: 'Jeans', ... }]
// } This replaces the reduce boilerplate we’ve all written dozens of times. At Expedia, we had a utility called groupBy that did exactly this — now it’s built in.
Map.groupBy
When you need non-string keys, use Map.groupBy:
const grouped = Map.groupBy(products, (product) =>
product.price > 500 ? 'premium' : 'budget'
);
grouped.get('premium'); // [Laptop, Phone]
grouped.get('budget'); // [Shirt, Jeans, Tablet] Promise.withResolvers
This is one of those features that eliminates an awkward pattern. Previously, to get external access to resolve and reject, you had to do this:
// Before ES2024
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
}); Now it’s clean:
// ES2024
const { promise, resolve, reject } = Promise.withResolvers();
// Use it in event-driven code
button.addEventListener('click', () => resolve('clicked'), { once: true });
const result = await promise; This is particularly useful for wrapping callback-based APIs or building custom async coordination patterns.
Real-World Example: Timeout Wrapper
function withTimeout(asyncFn, ms) {
const { promise: timeoutPromise, reject } = Promise.withResolvers();
const timer = setTimeout(() => reject(new Error('Timeout')), ms);
return Promise.race([
asyncFn().finally(() => clearTimeout(timer)),
timeoutPromise,
]);
}
// Usage
const data = await withTimeout(() => fetch('/api/data'), 5000); Well-Formed Unicode Strings
String.prototype.isWellFormed() and String.prototype.toWellFormed() help you deal with lone surrogates — characters that can cause issues in encodeURIComponent and other APIs.
const problematic = 'Hello � World';
problematic.isWellFormed(); // false
problematic.toWellFormed(); // 'Hello � World' (lone surrogate replaced)
// Safe encoding
const safeStr = input.isWellFormed() ? input : input.toWellFormed();
const encoded = encodeURIComponent(safeStr); // No more URIError At Tekion, we dealt with user-generated content from dealership forms in multiple languages. Malformed Unicode caused silent failures in our search indexing pipeline. These methods would have caught those issues early.
RegExp v Flag (Unicode Sets)
The new v flag replaces the u flag with extended capabilities for matching Unicode characters and set operations.
// Match any emoji
const emojiRegex = /p{Emoji}/v;
emojiRegex.test('👋'); // true
// Set subtraction: match Greek letters except specific ones
const regex = /[p{Script=Greek}--[αβγ]]/v;
regex.test('δ'); // true
regex.test('α'); // false
// Set intersection: match characters that are both ASCII and digits
const asciiDigits = /[p{ASCII}&&p{Number}]/v;
asciiDigits.test('5'); // true
asciiDigits.test('٥'); // false (Arabic-Indic digit) ArrayBuffer Transfer
ArrayBuffer.prototype.transfer() lets you efficiently move ownership of a buffer’s memory, similar to Rust’s ownership model.
const buffer = new ArrayBuffer(1024);
const transferred = buffer.transfer();
buffer.byteLength; // 0 (original is now detached)
transferred.byteLength; // 1024
// Resize during transfer
const resized = buffer.transfer(2048); This is useful in performance-critical scenarios like WebGL, audio processing, or working with large binary data in Web Workers.
Atomics.waitAsync
Atomics.waitAsync() provides non-blocking waiting on shared memory, enabling better coordination between the main thread and Web Workers.
const sharedBuffer = new SharedArrayBuffer(4);
const sharedArray = new Int32Array(sharedBuffer);
// Non-blocking wait on main thread
const result = Atomics.waitAsync(sharedArray, 0, 0);
result.value.then(() => {
console.log('Worker signaled completion');
});
// In worker: Atomics.notify(sharedArray, 0); Adoption Strategy
These features have strong browser support as of early 2025. Here is my recommendation for adopting them:
ADOPTION GUIDE
Not every ES2024 feature deserves the same rollout urgency. Some are immediate quality-of-life wins. Others depend more on your runtime targets.
USE NOW
High-confidence additions for everyday codebases
These features are the easiest to justify because they remove real boilerplate or failure modes with little conceptual overhead.
- `Object.groupBy` and `Map.groupBy`
- `Promise.withResolvers`
- `String.isWellFormed()` and `toWellFormed()`
EVALUATE FIRST
Features that depend more on runtime context
These are useful, but rollout should follow your browser support matrix or your worker and backend environment.
- RegExp `v` flag in browser-sensitive apps
- `ArrayBuffer.transfer()` for perf-heavy workloads
- `Atomics.waitAsync()` when you actually use shared memory and workers
Key Takeaways
Object.groupByeliminates one of the most common utility functions in JavaScript projectsPromise.withResolverscleans up the deferred promise pattern- Well-formed Unicode methods prevent silent encoding failures
- The RegExp
vflag enables powerful Unicode-aware pattern matching - These features are production-ready in modern browsers and Node.js 22+
Written by Umesh Malik
AI Engineer & Software Developer. Building GenAI applications, LLM-powered products, and scalable systems.
Related Articles

TypeScript
TypeScript Utility Types: A Complete Guide
Master TypeScript utility types including Partial, Required, Pick, Omit, Record, and more. Learn how to write cleaner, type-safe code with practical examples.

React
React Performance Optimization: 10 Proven Techniques
Learn 10 battle-tested React performance optimization techniques including memoization, code splitting, virtualization, and more from real enterprise applications.

SvelteKit
SvelteKit vs Next.js: A Comprehensive Comparison
An in-depth comparison of SvelteKit and Next.js covering performance, DX, routing, data fetching, and deployment. Based on real experience building with both.