TL;DR
ECMAScript 2025 chính thức đưa Iterator Helpers vào Iterator.prototype: .map(), .filter(), .take(), .drop(), .flatMap(), .reduce(), .forEach(), .some(), .every(), .find(), .toArray(). Tất cả chạy lazy — không tạo mảng trung gian, có thể xử lý stream vô hạn, dừng sớm khi đủ dữ liệu. Hỗ trợ Chrome 122+, Firefox 131+, Safari 18.4+, Node.js 22+. Proposal đã đạt Stage 4 và được merge vào ECMA-262.
Có gì mới
Trước ES2025, muốn xử lý lazy trong JavaScript bạn phải tự viết generator, dùng thư viện như RxJS hoặc Lodash, hoặc xài hack đủ kiểu. Giờ thì các method quen thuộc từ Array.prototype đã có phiên bản lazy gắn thẳng lên Iterator.prototype.
Điểm khác biệt cốt lõi: array method là eager — chạy hết toàn bộ mảng và tạo mảng mới ở mỗi bước chain. Iterator helper là lazy — không làm gì cho tới khi bạn yêu cầu giá trị, và mỗi item đi qua toàn bộ chain trước khi item tiếp theo được kéo ra.
// Eager — array methods
const result = users
.filter(u => u.isActive) // alloc array #1
.map(u => u.name) // alloc array #2
.slice(0, 2); // alloc array #3
// Lazy — iterator helpers
const result = Iterator.from(users)
.filter(u => u.isActive)
.map(u => u.name)
.take(2)
.toArray(); // dừng ngay khi đủ 2 phần tử
Vì sao quan trọng
Với dataset nhỏ vài trăm phần tử, khác biệt không đáng kể. Nhưng khi xử lý file lớn line-by-line, API phân trang, stream realtime, hoặc generator vô hạn, cách viết eager truyền thống sẽ ngốn RAM hoặc làm app crash. Iterator helpers cho bạn viết pipeline đẹp như array method nhưng có hiệu năng của generator thủ công.
Lazy không tự động đồng nghĩa với nhanh hơn. Nó chỉ thắng khi workload thực sự hưởng lợi từ việc trì hoãn — như dừng sớm, bỏ qua sớm, hoặc tránh tính toán không cần.
Bảng method
| Method | Trả về | Mô tả |
|---|---|---|
.map(fn) | Iterator | Transform mỗi giá trị |
.filter(fn) | Iterator | Giữ giá trị thỏa điều kiện |
.take(n) | Iterator | Lấy n phần tử đầu rồi dừng |
.drop(n) | Iterator | Bỏ n phần tử đầu, lấy phần còn lại |
.flatMap(fn) | Iterator | Map rồi flat một tầng |
.reduce(fn, init) | Value | Gộp về một giá trị |
.forEach(fn) | undefined | Side effect |
.some(fn) / .every(fn) | Boolean | Kiểm tra, dừng sớm |
.find(fn) | Value | Phần tử đầu thỏa, chạy được trên iterator vô hạn |
.toArray() | Array | Thu lại thành array thường |
Helper KHÔNG nằm trên Array.prototype. Để dùng với array, gọi arr.values() hoặc Iterator.from(arr) trước:
[1, 2, 3, 4, 5]
.values() // ⇐ chuyển sang iterator
.map(n => n * 2)
.filter(n => n > 4)
.toArray(); // [6, 8, 10]
So sánh với array methods
| Tình huống | Array methods | Iterator helpers |
|---|---|---|
| Dataset nhỏ, đã trong memory | ✅ Nhanh hơn (JIT, contiguous memory) | Overhead nhẹ |
| Chain nhiều bước trên dataset lớn | Tạo nhiều array trung gian | ✅ Không alloc trung gian |
Cần dừng sớm (.take, .find) | Vẫn xử lý hết rồi mới slice | ✅ Short-circuit |
| Stream vô hạn | ❌ Hang / crash | ✅ OK |
| Cần index / random access | ✅ Native | ❌ Chỉ tuần tự |
| Lặp nhiều lần | ✅ Tái dùng được | ❌ Single-use |
Use case thực tế
1. Stream vô hạn an toàn
function* naturals() {
let i = 1;
while (true) yield i++;
}
const tenEvens = naturals()
.filter(n => n % 2 === 0)
.take(10)
.toArray();
// [2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
2. API phân trang — chỉ kéo đủ dùng
async function* fetchPages(url) {
let next = url;
while (next) {
const res = await fetch(next).then(r => r.json());
yield* res.items;
next = res.nextUrl;
}
}
const first50 = await Iterator.from(fetchPages(API))
.filter(item => item.status === 'active')
.take(50)
.toArray();
// dừng fetch ngay khi đủ 50 active item
3. Chunk output LLM cho RAG
const chunks = Iterator.from(llmCompletions)
.flatMap(text => text.split(/[.!?]\s+/))
.map(s => s.trim())
.filter(s => s.length > 0)
.toArray();
// split + trim + filter trong một lượt lazy duy nhất
Hạn chế & lưu ý
- Single-use: iterator chỉ duyệt được một lần. Cần duyệt lại phải tạo iterator mới.
- Không có random access: nếu code cần truy cập theo index, dùng array.
- Lazy ≠ nhanh hơn mặc định: với dataset nhỏ đã trong memory, array methods thường thắng nhờ JIT tốt hơn và memory layout liền mạch.
- Không gọi trực tiếp trên array: phải
.values()hoặcIterator.from(arr). - Trộn iterator và array dễ nhầm:
iter.toArray()consume hết iterator — sau đó iterator rỗng. - Cho async iterator hiện vẫn là proposal riêng (Stage 2/3), chưa nằm trong ES2025.
Hỗ trợ & pricing
- Chrome 122+ (Feb 2024)
- Firefox 131+ (Oct 2024)
- Safari 18.4+ (đầu 2025)
- Node.js 22+ (Apr 2024)
- Polyfill cho môi trường cũ:
es-iterator-helperstrên npm - Miễn phí — feature ngôn ngữ, đã ratify trong ECMA-262
Sắp tới
Proposal kế tiếp đáng theo dõi: Iterator.zip() (experimental, gộp song song nhiều iterable thành tuple) và Async Iterator Helpers — cùng nhóm method nhưng cho AsyncIterator.prototype, đang ở Stage 2/3 trong TC39. Khi async helpers land, pipeline xử lý stream từ network/file system sẽ ngắn gọn không kém phiên bản đồng bộ.
Nguồn: MDN Iterator, TC39 proposal-iterator-helpers, V8 blog, LogRocket.