Đang tải...

Ếch Trendy

Cùng chú ếch nhỏ khám phá thế giới Web đầy biến động. Cập nhật trending nhanh như cách ếch đớp mồi! ⌨️🌿


1

ES2025 JavaScript: Patterns và Libraries Hot Nhất Cho Developer Việt 2026

Ếch Trendy
Ếch Trendy

6 tháng trước · 6 phút đọc

ES2025 ra mắt - cái gì thật sự vào spec?

Tháng 6/2025, TC39 chính thức công bố ECMAScript 2025 (ES16). Nếu bạn hay follow Twitter/X dev community, chắc nghe nhiều về pipeline operator hay Temporal API - nhưng cả hai vẫn chưa vào spec. Đừng để bị nhầm.

Cái thật sự vào ES2025 gồm 6 nhóm tính năng:

  • Iterator Helpers - global Iterator object với map, filter, take, drop
  • Set Methods - union(), intersection(), difference(), symmetricDifference()
  • Promise.try() - bắt cả sync lẫn async errors trong một chain
  • RegExp improvements - flag /v, RegExp.escape(), duplicate named groups
  • Import Attributes - import data from "./data.json" with { type: "json" }
  • Float16Array - typed array 16-bit cho memory-sensitive workloads

342865 ES2025 tập trung vào iterator, set, và async - không phải pipeline operator như nhiều bài viết nhầm

Node.js 22+ và các browser Chrome/Firefox/Safari release sau tháng 6/2025 đã support native. Không cần polyfill cho production nếu bạn target môi trường đủ mới.

Pipeline operator (|>) và Temporal API? Vẫn đang ở Stage 2-3. Dùng Luxon hoặc date-fns cho date/time là lựa chọn thực tế nhất hiện tại.


Iterator Helpers và Set Methods - dùng thật trong dự án thế nào?

Hai tính năng này mình thấy practical nhất trong ES2025 cho daily work.

Iterator Helpers giải quyết bài toán bạn từng phải import lodash chỉ để map + filter + slice một mảng lớn. Bây giờ không cần nữa:

// Trước ES2025 - load cả mảng vào memory
const result = apiData
  .filter(item => item.active)
  .map(item => item.name)
  .slice(0, 100);

// ES2025 - lazy evaluation, chỉ xử lý đến khi đủ 100 item
const result = Iterator.from(apiData)
  .filter(item => item.active)
  .map(item => item.name)
  .take(100)
  .toArray();

Khác biệt quan trọng: Iterator.from() lazy - với mảng 10.000 item nhưng chỉ cần 100 kết quả đầu, nó không xử lý 9.900 item còn lại. Lodash chain cũng làm được điều này, nhưng giờ là native.

342866 Lazy evaluation: chỉ xử lý đúng số item cần thiết, tiết kiệm CPU và memory đáng kể

Set Methods thì đặc biệt hữu ích cho permission systems hoặc dedup realtime data:

const userRoles = new Set(['admin', 'editor']);
const requiredRoles = new Set(['editor', 'viewer']);

// Kiểm tra overlap
const hasAccess = !userRoles.intersection(requiredRoles).size === 0;

// Chat app: merge messages từ 2 nguồn, không trùng
const allMessages = socketMessages.union(cachedMessages);

// Diff để biết message mới
const newMessages = socketMessages.difference(cachedMessages);

Trước đây bạn phải tự viết [...setA].filter(x => setB.has(x)). Dài, khó đọc, dễ bug. Set methods native clean hơn nhiều và chain được với nhau.


Promise.try() và async/await patterns nâng cao

Đây là tính năng mình mong nhất trong ES2025. Vấn đề cũ:

// Bug tiềm ẩn: nếu getUser() throw sync, lỗi không bị catch!
async function loadProfile(id) {
  return getUser(id) // sync function, throw không bị catch bởi .catch()
    .then(user => fetchDetails(user));
}

Promise.try() fix đúng điểm này:

// Promise.try wrap cả sync throw vào promise chain
Promise.try(() => getUser(id))
  .then(user => fetchDetails(user))
  .catch(err => handleError(err)); // bắt được cả sync lẫn async error

Pattern mạnh hơn: kết hợp với async generators cho streaming data

async function* streamDashboardData(endpoint) {
  const response = await Promise.try(() => fetch(endpoint));
  const reader = response.body.getReader();
  
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    
    // Dùng Iterator Helpers để transform ngay trên stream
    yield* Iterator.from([value])
      .map(chunk => JSON.parse(new TextDecoder().decode(chunk)))
      .filter(data => data.type === 'update');
  }
}

// Consume trong component
for await (const update of streamDashboardData('/api/stream')) {
  updateUI(update);
}

342867 Promise.try + async generator = xử lý stream data không bị miss error ở bất kỳ điểm nào

Top-level await (ES2022 nhưng hay dùng chung với ES2025 patterns) cũng clean hơn khi kết hợp Set ops:

// module.js - init một lần, share across module
const initialPermissions = new Set(await fetchUserPermissions());
const cachedPermissions = new Set(JSON.parse(localStorage.getItem('perms') ?? '[]'));

// Merge, loại bỏ duplicate
export const permissions = initialPermissions.union(cachedPermissions);

Nếu bạn muốn nền tảng JavaScript vững để apply những patterns này ngay, khóa JavaScript Pro cover sâu về async/await, closures, và cách JS engine thực sự hoạt động - đủ để đọc ES2025 spec mà hiểu rõ tại sao.


RxJS vs Zustand - chọn gì cho realtime web app?

Mình sẽ không nói "cái nào tốt hơn" vì câu trả lời phụ thuộc vào loại app bạn đang build.

RxJS (RxJS docs) là reactive programming library với Observable - phù hợp khi data flow của bạn phức tạp, nhiều nguồn, cần backpressure hay timing control:

import { fromEvent, Subject } from 'rxjs';
import { debounceTime, scan, filter } from 'rxjs/operators';

const socket = new WebSocket('wss://api.example.com/live');

// Stream WebSocket messages, debounce 300ms, accumulate
const liveData$ = fromEvent(socket, 'message').pipe(
  debounceTime(300),
  filter(event => event.data !== 'ping'),
  scan((acc, event) => [...acc.slice(-99), JSON.parse(event.data)], []),
);

liveData$.subscribe(messages => updateDashboard(messages));

Zustand (Zustand GitHub) đi theo hướng ngược lại - minimal, hooks-based, không boilerplate:

import { create } from 'zustand';

const useChatStore = create((set, get) => ({
  messages: [],
  connected: false,
  
  // ES2025 Promise.try trong action
  connectSocket: async (url) => {
    await Promise.try(async () => {
      const ws = new WebSocket(url);
      set({ connected: true });
      
      ws.onmessage = (e) => set(state => ({
        messages: [...state.messages, JSON.parse(e.data)]
      }));
    });
  },
}));

// Trong component
function ChatApp() {
  const { messages, connectSocket } = useChatStore();
  // ...
}

342868 RxJS cho stream phức tạp, Zustand cho React app state - chọn đúng tool cho đúng bài toán

Bảng so sánh thực tế:

Tiêu chí RxJS v8.x Zustand v5.x
Bundle size ~50KB <2KB
Learning curve Cao (marble diagrams) Thấp
Phù hợp cho Multiplayer, live feed, complex streams React SPA, UI state
ES2025 synergy Iterator.from(observable) Promise.try trong actions
TypeScript Tốt Rất tốt (generics cải thiện v5+)

Cho hầu hết React app Việt Nam (dashboard, e-commerce, admin panel) - Zustand là đủ và ít đau đầu hơn. RxJS chỉ worth it khi bạn xây multiplayer game, collaborative editor, hoặc financial live feed cần control timing cực chính xác.

Nếu bạn đang build với ReactJS và chưa rõ cách quản lý state từ cơ bản, khóa Xây Dựng Website với ReactJS sẽ giúp bạn hiểu đúng từ component state đến context trước khi nhảy vào Zustand.


Xây realtime dashboard với ES2025 + Zustand: ví dụ thực tế

Mình sẽ ghép tất cả những gì đã nói vào một mini case study: realtime stock price dashboard nhận data qua WebSocket.

Download file demo đầy đủ tại đây: Tải file demo Realtime Dashboard

Kiến trúc:

WebSocket server
    ↓
Zustand store (ES2025 Promise.try + Set dedup)
    ↓
Iterator Helpers (filter/transform)
    ↓
React components (subscribe minimal)

Store với ES2025 patterns:

import { create } from 'zustand';

const useStockStore = create((set, get) => ({
  prices: new Map(), // symbol -> latest price
  symbols: new Set(), // active symbols
  
  subscribeToSymbols: async (newSymbols) => {
    const current = get().symbols;
    // Set union - không duplicate
    const updated = current.union(new Set(newSymbols));
    set({ symbols: updated });
    
    // Promise.try bắt cả sync error từ WebSocket init
    await Promise.try(async () => {
      const ws = new WebSocket(`wss://api/stocks?symbols=${[...updated].join(',')}`);
      
      ws.onmessage = (e) => {
        const { symbol, price } = JSON.parse(e.data);
        set(state => ({
          prices: new Map(state.prices).set(symbol, price)
        }));
      };
    });
  },
  
  // Iterator Helpers: lấy top 10 cổ phiếu tăng nhiều nhất
  getTopGainers: (baseline) => {
    return Iterator.from(get().prices.entries())
      .filter(([, price]) => price > (baseline.get(symbol) ?? 0))
      .map(([symbol, price]) => ({ symbol, price }))
      .take(10)
      .toArray();
  },
}));

342869 Zustand store kết hợp ES2025 Set union và Iterator Helpers - ít code hơn, dễ đọc hơn

Pattern này cho phép bạn:

  • Không duplicate symbol khi user thêm subscription nhiều lần (Set.union)
  • Không bị missed error khi WebSocket init fail sync (Promise.try)
  • Chỉ xử lý đúng 10 item cần thiết từ hàng nghìn price entries (Iterator.take)

Bạn đã có đủ building blocks. Bước tiếp:

  1. Cài Node.js 22+ hoặc bật Chrome mới nhất để test native (không cần polyfill)
  2. Thêm Zustand vào project React hiện tại, migrate một slice state nhỏ trước
  3. Thử Iterator.from() thay lodash chain trong một data transformation function

Gặp vướng mắc ở phần nào, drop comment xuống dưới - mình đọc hết.