function* (function star) là từ khóa khai báo Generator Function (Hàm tạo sinh) trong JavaScript (ES6+). Khác với hàm thường chạy đến cuối rồi trả về một giá trị, Generator Function có thể tạm dừng (pause), trả về giá trị trung gian (yield), và tiếp tục (resume) sau đó từ nơi đã dừng.
Cú pháp
function* myGenerator() { ... }
// Hoặc
function *myGenerator() { ... }
// Hoặc (ít dùng)
function*myGenerator() { }
Đặc điểm cốt lõi
- Gọi hàm generator KHÔNG thực thi code ngay, mà trả về một Generator Object (Đối tượng tạo sinh).
- Generator Object tuân thủ Iterator Protocol (có phương thức
next(),return(),throw()). - Từ khóa yield tạm dừng hàm và trả về giá trị. Lần gọi
next()sau sẽ tiếp tục từ chỗyieldđó. - Khi hàm kết thúc (return hoặc hết code),
next()trả về{ value: undefined, done: true }.
Ví dụ cơ bản
function* numberGenerator() {
yield 1;
yield 2;
yield 3;
return "Kết thúc";
}
const gen = numberGenerator(); // Tạo generator object, CHƯA chạy code
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: "Kết thúc", done: true }
console.log(gen.next()); // { value: undefined, done: true }
Ví dụ thực tế: Quản lý luồng bất đồng bộ (Async Flow Control)
// Giả lập async operation
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
function* fetchUserData() {
console.log("1. Bắt đầu lấy user...");
const user = yield delay(1000).then(() => ({ id: 1, name: "An" }));
console.log("2. Đã có user:", user);
console.log("3. Bắt đầu lấy posts...");
const posts = yield delay(500).then(() => [{title: "Post 1"}, {title: "Post 2"}]);
console.log("4. Đã có posts:", posts);
return { user, posts };
}
// Runner đơn giản cho generator async
function runGenerator(genFunc) {
const gen = genFunc();
function next(value) {
const result = gen.next(value);
if (result.done) return Promise.resolve(result.value);
return Promise.resolve(result.value).then(next);
}
return next();
}
runGenerator(fetchUserData).then(finalResult => {
console.log("5. Hoàn tất:", finalResult);
});
/* Output:
1. Bắt đầu lấy user...
2. Đã có user: {id: 1, name: "An"}
3. Bắt đầu lấy posts...
4. Đã có posts: [...]
5. Hoàn tất: {user: {...}, posts: [...]}
*/
Các phương thức của Generator Object
| Phương thức | Chức năng |
|---|---|
next(value) | Tiếp tục thực thi, truyền value làm kết quả của biểu thức yield trước. Trả về {value, done}. |
return(value) | Buộc kết thúc generator, trả về {value, done: true}, chạy các finally block. |
throw(error) | Ném lỗi vào bên trong generator tại vị trí yield hiện tại, có thể bắt bằng try/catch bên trong generator. |
Yield* (Yield Delegate)
Để ủy thác cho generator/iterable khác:
function* inner() { yield 1; yield 2; }
function* outer() { yield 0; yield* inner(); yield 3; }
[...outer()] // [0, 1, 2, 3]
Ứng dụng chính
- Async/Await gốc: Generator + Promise runner là cơ chế cốt lõi của
async/await(Babel transpile async thành generator). - Lazy Evaluation (Tính toán lười): Xử lý dữ liệu lớn/ vô hạn (Infinite sequence) mà không tốn bộ nhớ.
- State Machine (Máy trạng thái): Code logic phức tạp dễ đọc hơn callback/Promise chain.
- Iterable tùy chỉnh: Triển khai
Symbol.iteratorcho object riêng. - Middleware (Redux Saga, Koa): Quản lý side effect phức tạp.
Lưu ý
- Generator không thể dùng arrow function (không có
function*arrow). yieldchỉ dùng được bên trong generator function.- Generator object chỉ dùng một lần (không reset được).
Tóm lại: function* tạo Generator - hàm có thể pause/resume bằng yield, trả về Iterator. Nền tảng của async/await, lý tưởng cho luồng bất đồng bộ phức tạp, lazy evaluation, state machine.