ES6: Cơ chế Iterator và Generator trong JavaScript

Iterator là một cơ chế duyệt mới được giới thiệu trong ES6, cung cấp giao diện thống nhất giúp truy cập thuận tiện vào các cấu trúc dữ liệu khác nhau. Cốt lõi của nó dựa trên phương thức có key là Symbol.iterator.

Iterator hoạt động như một con trỏ (tương tự cursor trong cơ sở dữ liệu) để duyệt qua các phần tử của cấu trúc dữ liệu.

// Tạo iterator từ mảng
const data = ['red', 'green', 'blue'];
const iterator = data[Symbol.iterator]();
console.log(iterator); // Array Iterator{}

// Dùng next() để duyệt từng bước
let step = iterator.next();
console.log(step); // {value: 'red', done: false}

// Khi done = true, quá trình duyệt kết thúc

Các cấu trúc dữ liệu có thể duyệt (iterable) trong ES6 bao gồm: Array, String, Map, Set.

Lưu ý: Object không hỗ trợ trực tiếp iterator, do đó không thể dùng vòng lặp for..of với object thông thường. Để sử dụng, cần chuyển object thành mảng.

// Chuyển object thành mảng để có thể duyệt
const objData = {"length": 3, 0: "first", 1: "second"};
const arrayFromObj = Array.from(objData);
console.log(arrayFromObj); // ['first', 'second', undefined]

for (let item of arrayFromObj) {
    console.log(item); // first, second, undefined
}

Generator (Hàm tạo)

ES6 giới thiệu Generator function, với khả năng tạm dừng luồng thực thi nhờ từ khóa yield. Điều này mở ra hướng tiếp cận mới cho lập trình bất đồng bộ.

Đặc điểm nhận dạng Generator function:

  • Có dấu * ngay sau từ khóa function.
  • Bên trong sử dụng biểu thức yield.
// Định nghĩa Generator function
function* genFunc(start) {
    console.log("Bắt đầu");
    yield start;
    console.log("Bước 2");
    yield start + 10;
    console.log("Kết thúc");
    return 100;
}

// Gọi hàm trả về iterator object
let genObj = genFunc(5);
console.log(genObj); // genFunc{<suspended>}

// Dùng next() để chạy từng bước
console.log(genObj.next()); // {value: 5, done: false}
console.log(genObj.next()); // {value: 15, done: false}
console.log(genObj.next()); // {value: 100, done: true}
console.log(genObj.next()); // {value: undefined, done: true}

Generator function thực thi theo từng đoạn: yield tạm dừng, next() tiếp tục.

function* calculator() {
    console.log('Khởi động');
    let a = yield 'Nhập A';
    console.log('Giá trị A: ' + a);
    let b = yield 'Nhập B';
    console.log('Giá trị B: ' + b);
    let sum = a + b;
    console.log('Tổng: ' + sum);
    return sum;
}

let calc = calculator();
console.log(calc.next(0));    // Bắt đầu, {value: 'Nhập A', done: false}
console.log(calc.next(10));   // A = 10, {value: 'Nhập B', done: false}
console.log(calc.next(20));   // B = 20, Tổng: 30, {value: 30, done: true}

Phương thức return() kết thúc Generator ngay lập tức và trả về giá trị được chỉ định (hoặc undefined nếu không có tham số).

// Cung cấp iterator cho object bằng Generator
function* objIterator(obj) {
    const keys = Object.keys(obj);
    for (let key of keys) {
        yield [key, obj[key]];
    }
}

const person = { name: 'Alice', age: 25 };

// Gán iterator
person[Symbol.iterator] = objIterator;

for (let [key, value] of objIterator(person)) {
    console.log(`${key}: ${value}`);
}
// name: Alice
// age: 25

Ứng dụng Generator trong xử lý bất đồng bộ

// Xử lý AJAX bằng Generator - viết code đồng bộ nhưng chạy bất đồng bộ
function* requestHandler() {
    const data = yield fetchData('https://api.example.com/data');
    console.log('Dữ liệu nhận được:', data);
    console.log('Xử lý tiếp...');
}

let handler = requestHandler();
handler.next();

function fetchData(url) {
    // Giả lập gọi AJAX
    $.ajax({
        url: url,
        method: 'GET',
        success(response) {
            handler.next(response); // Trả kết quả về Generator
        }
    });
}

// Mô hình quản lý UI với Generator
function* uiController() {
    showLoading();
    yield loadContent();
    hideLoading();
}

let controller = uiController();
controller.next(); // Hiện loading + bắt đầu tải dữ liệu

function showLoading() {
    console.log('Hiển thị màn hình chờ...');
}

function loadContent() {
    setTimeout(() => {
        console.log('Dữ liệu đã tải xong');
        controller.next(); // Ẩn loading sau khi tải xong
    }, 2000);
}

function hideLoading() {
    console.log('Ẩn màn hình chờ');
}

Thẻ: Iterator generator yield JavaScript ES6 for-of

Đăng vào ngày 27 tháng 7 lúc 13:01