What’s New in ECMAScript 2024? A Deep Dive into the Latest Proposals
The ECMAScript 2024 specification, approved on June 26, introduces six major proposals—including well‑formed Unicode strings, asynchronous atomic wait, a powerful RegExp v flag, ArrayBuffer transfer, native array grouping, and Promise.withResolvers—each illustrated with code examples and practical implications for JavaScript developers.
ECMAScript 2024 (https://tc39.es/ecma262/2024/) was approved on June 26. The following proposals were incorporated into the final language specification.
Well‑Formed Unicode Strings
JavaScript strings are sequences of UTF‑16 code units (65 536 possible values). Characters outside the Basic Multilingual Plane are represented as surrogate pairs. An isolated surrogate makes a string malformed. The proposal adds String.prototype.isWellFormed() to test for well‑formedness and String.prototype.toWellFormed() to replace isolated surrogates with the replacement character U+FFFD.
'a'.length // 1
'a'.split('') // ['a']
'🥑'.length // 2
'🥑'.split('') // ['\ud83e', '\udd51'] // surrogate pair
'\ud83e\udd51'.isWellFormed() // true
'\ud83e'.isWellFormed() // false
'\ud83e'.toWellFormed() // �Proposal address: https://github.com/tc39/proposal-well-formed-string
Asynchronous Atomic Wait
Workers provide multithreading in JavaScript via SharedArrayBuffer. The synchronous Atomics.wait() cannot be used on the main thread. The new Atomics.waitAsync() method allows waiting without blocking the main thread.
// main thread
let i32a = null;
const w = new Worker("worker.js");
w.onmessage = function (env) { i32a = env.data };
setTimeout(() => {
Atomics.store(i32a, 0, 1);
Atomics.notify(i32a, 0);
}, 1000); // worker thread
const sab = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT);
const i32a = new Int32Array(sab);
postMessage(i32a);
const wait = Atomics.waitAsync(i32a, 0, 0);
// wait may be { async: false, value: "not-equal" | "timed-out" }
// or { async: true, value: Promise<"ok" | "timed-out"> }
if (wait.async) {
wait.value.then(v => console.log(v));
} else {
console.log(wait.value);
}Proposal address: https://github.com/tc39/proposal-atomics-wait-async
RegExp v Flag with Set Notation and String Properties
The v flag extends the 2015 u flag with Unicode property checks, set‑notation operations (subtraction --, intersection &&, union), and a literal string syntax \q. It cannot be combined with u.
const pattern = /./vu; // SyntaxError: invalid flagsUnicode property checks: /\p{Math}/u – matches mathematical symbols /\p{Dash}/u – matches dash punctuation /\p{ASCII_Hex_Digit}/u – matches hexadecimal digits
const patternMath = /\p{Math}/u;
patternMath.test('+'); // true
patternMath.test('z'); // falseSet operations and literal strings:
const pattern = /[\p{RGI_Emoji}--\q{💩}]/v;
pattern.test('😜'); // true
pattern.test('💩'); // falseExample of intersecting uppercase letters with hexadecimal digits:
const pattern = /[\p{Uppercase}&&\p{ASCII_Hex_Digit}]/v;
pattern.test('f'); // true
pattern.test('F'); // falseProposal address: https://github.com/tc39/proposal-regexp-v-flag
ArrayBuffer Transfer
The proposal adds ArrayBuffer.prototype.transfer() and ArrayBuffer.prototype.transferToFixedLength() to move byte data between locations. A new detached getter reports whether a buffer has been transferred.
const buffer = new ArrayBuffer();
buffer.detached; // false
const newBuffer = buffer.transfer();
buffer.detached; // trueProposal address: https://github.com/tc39/proposal-arraybuffer-transfer
Array Grouping
The familiar groupBy utility from Lodash/Ramda becomes a native static method on Object and Map. The proposal originally considered Array.prototype.groupBy but chose static methods to avoid conflict with existing sugar.
const langs = [
{ name: "Rust", compiled: true, released: 2015 },
{ name: "Go", compiled: true, released: 2009 },
{ name: "JavaScript", compiled: false, released: 1995 },
{ name: "Python", compiled: false, released: 1991 }
];
const callback = ({ compiled }) => compiled ? "compiled" : "interpreted";
const langsByType = Object.groupBy(langs, callback);
console.log({ langsByType });
// { compiled: [{...}, {...}], interpreted: [{...}, {...}] }Proposal address: https://github.com/tc39/proposal-array-grouping
Promise.withResolvers
The proposal introduces Promise.withResolvers(), which returns an object containing a promise and its external resolve and reject functions. This enables patterns where resolution or rejection occurs from multiple places.
function createEventsAggregator(eventsCount) {
const events = [];
const { promise, resolve, reject } = Promise.withResolvers();
return {
add: (event) => {
if (events.length < eventsCount) events.push(event);
if (events.length === eventsCount) resolve(events);
},
abort: () => reject("Events aggregation aborted."),
events: promise
};
}
const eventsAggregator = createEventsAggregator(3);
eventsAggregator.events
.then(events => console.log("Resolved:", events))
.catch(reason => console.error("Rejected:", reason));
eventsAggregator.add("event-one");
eventsAggregator.add("event-two");
eventsAggregator.add("event-three");
// Resolved: ["event-one", "event-two", "event-three"]Proposal address: https://github.com/tc39/proposal-promise-with-resolvers
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Full-Stack Cultivation Path
Focused on sharing practical tech content about TypeScript, Vue 3, front-end architecture, and source code analysis.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
