jvinhit//lab

Search posts

Type to search across journal entries.

navigate open esc close

What's New in ES2025 & ES2026 — The Complete Guide with Examples

A bilingual, example-driven tour of ECMAScript 2025 (Iterator Helpers, Set methods, Promise.try, RegExp.escape, import attributes, Float16Array) and ES2026 (explicit resource management with using, Array.fromAsync, Error.isError).

The Annual Release Cycle {Chu kỳ phát hành hàng năm}

Since ES2016, TC39 ships a new ECMAScript edition every June {Từ ES2016, TC39 phát hành một phiên bản ECMAScript mới mỗi tháng Sáu}. A proposal becomes part of the language once it reaches Stage 4 {Một proposal trở thành một phần của ngôn ngữ khi đạt Stage 4}.

  • ES2025 (the 16th edition) was finalized on 25 June 2025 {ES2025 (phiên bản thứ 16) được chốt vào 25/06/2025}.
  • ES2026 is on track to be approved around June 2026 {ES2026 đang trên đà được duyệt vào khoảng tháng 6/2026}; the features below already reached Stage 4 and ship in modern engines {các tính năng dưới đây đã đạt Stage 4 và có mặt trong các engine hiện đại}.

This is a hands-on tour — every feature has a runnable example {Đây là chuyến đi thực hành — mỗi tính năng đều có ví dụ chạy được}.

Naming note {Ghi chú tên gọi}: “ESxxxx” refers to the year the spec is published, not when engines implement it {“ESxxxx” chỉ năm spec được xuất bản, không phải khi engine triển khai}. Many features land in browsers before the edition is formally approved {Nhiều tính năng đến với trình duyệt trước khi phiên bản được duyệt chính thức}.


Part 1 — ECMAScript 2025 {Phần 1 — ECMAScript 2025}

Iterator Helpers — Lazy Array Methods {Iterator Helpers — Phương thức kiểu Array nhưng lười}

The biggest ergonomic win in ES2025 {Cải thiện trải nghiệm lớn nhất trong ES2025}. Iterators now have .map(), .filter(), .take(), and friends — but unlike arrays, they’re lazy {Iterator giờ có .map(), .filter(), .take(),… — nhưng khác array, chúng lười (lazy)}.

function* naturals() {
  let n = 1;
  while (true) yield n++; // infinite!
}

const result = naturals()
  .map((n) => n ** 2)   // 1, 4, 9, 16, ...
  .filter((n) => n % 2) // keep odds
  .take(5)              // stop after 5
  .toArray();           // only NOW does work happen

console.log(result); // [1, 9, 25, 49, 81]

Why this matters {Tại sao quan trọng}: with arrays, [...infinite].map().filter() would hang forever because each step builds a full intermediate array {với array, [...infinite].map().filter() sẽ treo mãi vì mỗi bước dựng một array trung gian đầy đủ}. Iterator helpers process one element at a time, on demand, so they work on infinite or huge sequences and avoid intermediate allocations {Iterator helper xử lý từng phần tử một, theo yêu cầu, nên chạy được trên chuỗi vô hạn hay khổng lồ và tránh cấp phát trung gian}.

Available helpers {Các helper có sẵn}: map, filter, flatMap, take, drop, reduce, toArray, forEach, some, every, find, plus the static Iterator.from() {cùng phương thức tĩnh Iterator.from()}.

// Iterator.from() wraps any iterable so you can chain helpers on it
const firstThree = Iterator.from(['a', 'b', 'c', 'd']).take(3).toArray();
console.log(firstThree); // ['a', 'b', 'c']

Set Methods — Real Set Algebra {Set Methods — Đại số tập hợp thật sự}

Set finally gets the mathematical operations we always reimplemented by hand {Set cuối cùng đã có các phép toán mà ta luôn phải tự cài lại bằng tay}.

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 b)
a.symmetricDifference(b);// Set {1, 2, 5, 6}  (in exactly one)

a.isSubsetOf(b);         // false
a.isSupersetOf(new Set([1, 2])); // true
a.isDisjointFrom(new Set([7, 8])); // true (no overlap)

These return new Sets (non-mutating) and accept any set-like argument {Chúng trả về Set mới (không biến đổi) và nhận bất kỳ đối số dạng set nào}. No more new Set([...a].filter(x => b.has(x))) {Không còn phải viết new Set([...a].filter(x => b.has(x)))}.


Promise.try — Start a Chain Safely {Promise.try — Bắt đầu chuỗi một cách an toàn}

Promise.try(fn) runs fn and always returns a Promise — whether fn returns a value, returns a promise, or throws synchronously {Promise.try(fn) chạy fnluôn trả về Promise — dù fn trả giá trị, trả promise, hay throw đồng bộ}.

// Problem: a sync throw escapes the promise chain
function getConfig(raw) {
  if (!raw) throw new Error('missing config'); // sync throw!
  return JSON.parse(raw);
}

// ❌ The throw happens BEFORE any .catch() can catch it
getConfig(input).then(use).catch(handle); // may crash synchronously

// ✅ Promise.try funnels sync + async errors into the same chain
Promise.try(() => getConfig(input))
  .then(use)
  .catch(handle); // catches the sync throw too

It’s the standardized version of a pattern libraries like Bluebird popularized {Đây là phiên bản chuẩn hóa của một mẫu mà các thư viện như Bluebird đã phổ biến}.


RegExp.escape — Safe Dynamic Patterns {RegExp.escape — Pattern động an toàn}

Building a regex from user input was always risky — special characters like . or ( change the meaning {Dựng regex từ input người dùng luôn rủi ro — ký tự đặc biệt như . hay ( làm đổi ý nghĩa}. RegExp.escape() neutralizes them {RegExp.escape() vô hiệu hóa chúng}.

const userInput = 'a.b(c)';

// ❌ '.' matches any char, '()' is a group → wrong + injection risk
new RegExp(userInput);

// ✅ escape first
const safe = RegExp.escape(userInput); // 'a\\.b\\(c\\)'
const re = new RegExp(safe);
console.log(re.test('a.b(c)')); // true — matches literally

Regex: Inline Modifiers + Duplicate Named Groups {Regex: Cờ inline + nhóm tên trùng}

Inline pattern modifiers let you enable/disable flags for part of a pattern {Cờ inline cho phép bật/tắt flag cho một phần của pattern}:

// (?i:...) → case-insensitive ONLY inside the group
const re = /hello-(?i:world)/;
re.test('hello-WORLD'); // true  ('world' part is case-insensitive)
re.test('HELLO-world'); // false ('hello' part is still case-sensitive)

Duplicate named capture groups are now allowed across alternatives {Nhóm tên trùng giờ được phép giữa các nhánh alternation}:

// Previously a SyntaxError; now valid because the names are in
// mutually-exclusive branches of the alternation.
const date = /(?<year>\d{4})-\d{2}|\d{2}-(?<year>\d{4})/;
'2026-05'.match(date).groups.year; // '2026'
'05-2026'.match(date).groups.year; // '2026'

Import Attributes & JSON Modules {Import Attributes & JSON Modules}

You can now import JSON natively, declaring the type with the with keyword {Giờ bạn có thể import JSON một cách gốc, khai báo type bằng từ khóa with}:

// Static import
import config from './config.json' with { type: 'json' };

// Dynamic import
const data = await import('./data.json', { with: { type: 'json' } });

console.log(config.version);

The with { type: 'json' } attribute tells the runtime how to parse and validate the module {Attribute with { type: 'json' } báo cho runtime cách parse và validate module}. This is a security feature too — the runtime won’t execute a .json URL as JavaScript {Đây cũng là tính năng bảo mật — runtime sẽ không thực thi một URL .json như JavaScript}. JSON is the first supported type; more will follow {JSON là type đầu tiên được hỗ trợ; sẽ có thêm sau}.


Float16Array — Half-Precision Floats {Float16Array — Số thực nửa độ chính xác}

A new typed array for 16-bit floats, useful for GPU/ML workloads, graphics, and bandwidth-sensitive data {Một typed array mới cho số thực 16-bit, hữu ích cho GPU/ML, đồ họa, và dữ liệu nhạy băng thông}.

const floats = new Float16Array([1.5, 2.25, 3.125]);
console.log(floats[0]); // 1.5 (stored in 2 bytes instead of 4 or 8)

// Companion APIs:
const view = new DataView(new ArrayBuffer(2));
view.setFloat16(0, 3.14);
view.getFloat16(0);   // ~3.14 (rounded to half precision)
Math.f16round(3.14159); // nearest value representable as a float16

Half precision trades accuracy for half the memory of Float32Array — a common tradeoff in machine learning tensors {Nửa độ chính xác đánh đổi độ chính xác lấy một nửa bộ nhớ so với Float32Array — đánh đổi phổ biến trong tensor học máy}.


Part 2 — ECMAScript 2026 {Phần 2 — ECMAScript 2026}

These reached Stage 4 and ship in current engines; they’re slated for the ES2026 edition (≈ June 2026) {Những thứ này đã đạt Stage 4 và có trong các engine hiện tại; dự kiến vào phiên bản ES2026 (≈ tháng 6/2026)}.

Explicit Resource Management — using & await using {Quản lý tài nguyên tường minh — using & await using}

The headline of ES2026 {Tính năng đinh của ES2026}. The new using declaration automatically disposes a resource when it goes out of scope — like a destructor, or Python’s with, or C#‘s using {Khai báo using mới tự động dọn dẹp tài nguyên khi ra khỏi scope — như destructor, như with của Python, hay using của C#}.

A disposable resource defines a [Symbol.dispose]() method {Một tài nguyên disposable định nghĩa phương thức [Symbol.dispose]()}:

function openFile(path) {
  const handle = acquireHandle(path);
  return {
    handle,
    read() { return readFrom(this.handle); },
    [Symbol.dispose]() {
      releaseHandle(handle); // runs automatically at scope exit
    },
  };
}

function process() {
  using file = openFile('/tmp/data');
  const text = file.read();
  return text.toUpperCase();
  // No try/finally needed — file[Symbol.dispose]() runs here,
  // even if an error is thrown above.
}

For async resources (network connections, async DB handles), use await using with [Symbol.asyncDispose]() {Với tài nguyên bất đồng bộ (kết nối mạng, handle DB async), dùng await using với [Symbol.asyncDispose]()}:

async function query() {
  await using conn = await db.connect(); // has [Symbol.asyncDispose]
  return conn.run('SELECT 1');
  // await conn[Symbol.asyncDispose]() runs (and is awaited) on exit.
}

Supporting pieces {Các mảnh hỗ trợ}:

  • DisposableStack / AsyncDisposableStack — containers to aggregate multiple resources and dispose them all together (in reverse order) {container gom nhiều tài nguyên và dọn tất cả cùng lúc (theo thứ tự ngược)}.
  • SuppressedError — when a cleanup throws while another error is already propagating, both are preserved (the new one as .error, the original as .suppressed) {khi việc dọn dẹp throw trong lúc một lỗi khác đang lan, cả hai được giữ lại}.

Constraint {Ràng buộc}: using / await using are block-scoped only — inside {}, loops, or function bodies — not at the top level of a module {using / await using chỉ trong block — bên trong {}, vòng lặp, hay thân hàm — không dùng ở top-level của module}.

This replaces the error-prone try { ... } finally { cleanup() } dance for any resource with a lifecycle {Nó thay thế điệu nhảy dễ sai try { ... } finally { cleanup() } cho mọi tài nguyên có vòng đời}.


Array.fromAsync — Array.from for Async Iterables {Array.fromAsync — Array.from cho async iterable}

Array.from doesn’t understand async iterables {Array.from không hiểu async iterable}. Array.fromAsync does — it awaits each value and collects them {Array.fromAsync thì có — nó await từng giá trị rồi gom lại}.

async function* fetchPages() {
  yield await fetch('/api/page/1').then((r) => r.json());
  yield await fetch('/api/page/2').then((r) => r.json());
}

// await each yielded value, build an array
const pages = await Array.fromAsync(fetchPages());
console.log(pages.length); // 2

// also works on an array of promises:
const data = await Array.fromAsync([fetch('/a'), fetch('/b')]);

It’s the missing async counterpart to Array.from, and it processes values sequentially (unlike Promise.all, which is parallel) {Đây là phiên bản async còn thiếu của Array.from, và xử lý giá trị tuần tự (khác Promise.all vốn song song)}.


Error.isError — Reliable Error Detection {Error.isError — Phát hiện Error đáng tin}

value instanceof Error lies across realms (iframes, worker threads, vm contexts) and can be fooled by fake prototypes {value instanceof Error nói dối khi qua realm (iframe, worker, vm context) và có thể bị lừa bằng prototype giả}. Error.isError() checks the real internal brand {Error.isError() kiểm tra “nhãn” nội bộ thật sự}.

Error.isError(new Error('x'));     // true
Error.isError(new TypeError('y')); // true

// Cases where instanceof fails but isError is correct:
Error.isError({ name: 'Error', message: 'fake' }); // false (just an object)
// An Error from another iframe/realm:
Error.isError(errorFromIframe);    // true (instanceof would be false)

Reach for it in libraries, error-normalization layers, and anywhere errors may cross context boundaries {Dùng nó trong thư viện, lớp chuẩn hóa lỗi, và bất kỳ đâu lỗi có thể vượt ranh giới context}.


How to Use These Today {Cách dùng ngay hôm nay}

  1. Check support {Kiểm tra hỗ trợ}: most ES2025 features are already in current Chrome, Firefox, Safari, Node, Deno, and Bun {phần lớn tính năng ES2025 đã có trong Chrome, Firefox, Safari, Node, Deno, Bun bản hiện tại}. ES2026 items (using, Array.fromAsync, Error.isError) are shipping but check your minimum target {các mục ES2026 đang được tung ra nhưng hãy kiểm tra target tối thiểu của bạn}.
  2. Transpile {Transpile}: TypeScript 5.2+ supports using/await using; Babel and @babel/preset-env down-level most syntax {TypeScript 5.2+ hỗ trợ using/await using; Babel hạ cấp được phần lớn cú pháp}.
  3. Polyfill {Polyfill}: core-js covers Iterator Helpers, Set methods, Promise.try, Array.fromAsync, and more for older runtimes {core-js bao gồm Iterator Helpers, Set methods, Promise.try, Array.fromAsync,… cho runtime cũ}.

Quick Reference {Tham chiếu nhanh}

Feature {Tính năng}EditionOne-liner {Tóm tắt}
Iterator HelpersES2025Lazy .map/.filter/.take on iterators
Set methodsES2025union / intersection / difference / …
Promise.tryES2025Sync + async errors in one chain
RegExp.escapeES2025Escape strings for dynamic regex
Regex inline flagsES2025(?i:...) per-group modifiers
Import attributesES2025import x from './a.json' with { type: 'json' }
Float16ArrayES2025Half-precision typed array
using / await usingES2026Auto-dispose resources at scope exit
Array.fromAsyncES2026Array.from for async iterables
Error.isErrorES2026Realm-safe Error check

JavaScript keeps absorbing the patterns we used to pull from libraries — lazy iteration, set algebra, safe regex building, deterministic cleanup {JavaScript tiếp tục hấp thụ những mẫu mà ta từng phải lấy từ thư viện — lặp lười, đại số tập hợp, dựng regex an toàn, dọn dẹp xác định}. The fewer dependencies for these basics, the better {Càng ít dependency cho những thứ cơ bản này càng tốt}.

Related {Liên quan}: JavaScript Generators & yield · Unicode, Strings & Binary in JavaScript.