function*
Baseline
Widely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since September 2016.
Khai báo function* tạo ra một binding của một generator function mới với tên đã cho. Một generator function có thể thoát ra và sau đó được vào lại, với ngữ cảnh của nó (bindings biến) được lưu trữ qua các lần vào lại.
Bạn cũng có thể định nghĩa generator function bằng cách sử dụng function* expression.
Try it
function* generator(i) {
yield i;
yield i + 10;
}
const gen = generator(10);
console.log(gen.next().value);
// Expected output: 10
console.log(gen.next().value);
// Expected output: 20
Cú pháp
function* name(param0) {
statements
}
function* name(param0, param1) {
statements
}
function* name(param0, param1, /* …, */ paramN) {
statements
}
Note: Generator function không có dạng arrow function tương ứng.
Note:
function và * là các token riêng biệt, vì vậy chúng có thể được phân cách bởi khoảng trắng hoặc ký tự kết thúc dòng.
Tham số
name-
Tên của function.
paramOptional-
Tên của tham số chính thức cho function. Về cú pháp của tham số, xem tài liệu tham khảo Functions.
statementsOptional-
Các câu lệnh tạo nên phần thân của function.
Mô tả
Khai báo function* tạo ra một đối tượng GeneratorFunction. Mỗi lần generator function được gọi, nó trả về một đối tượng Generator mới, tuân theo iterator protocol. Quá trình thực thi của generator function bị tạm dừng tại một vị trí nào đó, ban đầu là ở chính đầu phần thân function. Generator function có thể được gọi nhiều lần để tạo ra nhiều generator đồng thời; mỗi generator duy trì execution context riêng của nó cho generator function và có thể được thực thi độc lập.
Generator cho phép luồng điều khiển hai chiều: luồng điều khiển có thể chuyển giữa generator function (callee) và caller bao nhiêu lần tùy thích. Luồng điều khiển có thể đi từ caller đến callee bằng cách gọi các phương thức của generator: next(), throw(), và return(). Luồng điều khiển có thể đi từ callee đến caller bằng cách thoát function bình thường bằng return hoặc throw hoặc thực thi hết tất cả câu lệnh, hoặc bằng cách sử dụng biểu thức yield và yield*.
Khi phương thức next() của generator được gọi, phần thân của generator function được thực thi cho đến khi xảy ra một trong những điều sau:
- Một biểu thức
yield. Trong trường hợp này, phương thứcnext()trả về một đối tượng có thuộc tínhvaluechứa giá trị được yield và thuộc tínhdoneluôn làfalse. Lần tiếp theonext()được gọi, biểu thứcyieldsẽ được đánh giá thành giá trị được truyền vàonext(). - Một
yield*, ủy quyền cho một iterator khác. Trong trường hợp này, lần gọi này và bất kỳ lần gọi nào trong tương lai đếnnext()trên generator đều giống như gọinext()trên iterator được ủy quyền, cho đến khi iterator được ủy quyền kết thúc. - Một câu lệnh
return(không bị chặn bởitry...catch...finally), hoặc kết thúc luồng điều khiển ngầm định làreturn undefined. Trong trường hợp này, generator kết thúc, và phương thứcnext()trả về một đối tượng có thuộc tínhvaluechứa giá trị được trả về và thuộc tínhdoneluôn làtrue. Bất kỳ lần gọinext()nào tiếp theo đều không có tác dụng và luôn trả về{ value: undefined, done: true }. - Một lỗi được ném ra bên trong function, thông qua câu lệnh
throwhoặc một ngoại lệ không được xử lý. Phương thứcnext()ném lỗi đó, và generator kết thúc. Bất kỳ lần gọinext()nào tiếp theo đều không có tác dụng và luôn trả về{ value: undefined, done: true }.
Khi phương thức throw() của generator được gọi, nó hoạt động như thể câu lệnh throw được chèn vào thân của generator tại vị trí hiện tại đang bị tạm dừng. Tương tự, khi phương thức return() của generator được gọi, nó hoạt động như thể câu lệnh return được chèn vào thân của generator tại vị trí hiện tại đang bị tạm dừng. Cả hai phương thức thường kết thúc generator, trừ khi generator function bắt sự kiện hoàn thành qua try...catch...finally.
Generator từng là một mô hình lập trình bất đồng bộ, tránh Callback Hell bằng cách đạt được Inversion of Control. Ngày nay, trường hợp sử dụng này được giải quyết với mô hình async function đơn giản hơn và đối tượng Promise. Tuy nhiên, generator vẫn hữu ích cho nhiều tác vụ khác, chẳng hạn như định nghĩa iterator theo cách đơn giản.
Khai báo function* hoạt động tương tự như khai báo function — chúng được hoisted lên đầu phạm vi của chúng và có thể được gọi ở bất kỳ đâu trong phạm vi đó, và chúng chỉ có thể được khai báo lại trong một số ngữ cảnh nhất định.
Ví dụ
>Ví dụ cơ bản
function* idMaker() {
let index = 0;
while (true) {
yield index++;
}
}
const gen = idMaker();
console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // 3
// …
Ví dụ với yield*
function* anotherGenerator(i) {
yield i + 1;
yield i + 2;
yield i + 3;
}
function* generator(i) {
yield i;
yield* anotherGenerator(i);
yield i + 10;
}
const gen = generator(10);
console.log(gen.next().value); // 10
console.log(gen.next().value); // 11
console.log(gen.next().value); // 12
console.log(gen.next().value); // 13
console.log(gen.next().value); // 20
Truyền đối số vào Generator
function* logGenerator() {
console.log(0);
console.log(1, yield);
console.log(2, yield);
console.log(3, yield);
}
const gen = logGenerator();
// lần gọi next đầu tiên thực thi từ đầu function
// đến câu lệnh yield đầu tiên
gen.next(); // 0
gen.next("pretzel"); // 1 pretzel
gen.next("california"); // 2 california
gen.next("mayonnaise"); // 3 mayonnaise
Câu lệnh return trong generator
function* yieldAndReturn() {
yield "Y";
return "R";
yield "unreachable";
}
const gen = yieldAndReturn();
console.log(gen.next()); // { value: "Y", done: false }
console.log(gen.next()); // { value: "R", done: true }
console.log(gen.next()); // { value: undefined, done: true }
Generator là thuộc tính của object
const someObj = {
*generator() {
yield "a";
yield "b";
},
};
const gen = someObj.generator();
console.log(gen.next()); // { value: 'a', done: false }
console.log(gen.next()); // { value: 'b', done: false }
console.log(gen.next()); // { value: undefined, done: true }
Generator là phương thức của object
class Foo {
*generator() {
yield 1;
yield 2;
yield 3;
}
}
const f = new Foo();
const gen = f.generator();
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: undefined, done: true }
Generator là computed property
class Foo {
*[Symbol.iterator]() {
yield 1;
yield 2;
}
}
const SomeObj = {
*[Symbol.iterator]() {
yield "a";
yield "b";
},
};
console.log(Array.from(new Foo())); // [ 1, 2 ]
console.log(Array.from(SomeObj)); // [ 'a', 'b' ]
Generator không thể được khởi tạo bằng new
function* f() {}
const obj = new f(); // throws "TypeError: f is not a constructor
Ví dụ về generator
function* powers(n) {
// Vòng lặp vô hạn để tạo ra
for (let current = n; ; current *= n) {
yield current;
}
}
for (const power of powers(2)) {
// Kiểm soát generator
if (power > 32) {
break;
}
console.log(power);
// 2
// 4
// 8
// 16
// 32
}
Đặc tả
| Specification |
|---|
| ECMAScript® 2027 Language Specification> # sec-generator-function-definitions> |
Tương thích trình duyệt
Xem thêm
- Hướng dẫn Functions
- Hướng dẫn Iterators and generators
- Functions
GeneratorFunctionfunction*expressionfunctionasync functionasync function*- Iteration protocols
yieldyield*Generator- Regenerator trên GitHub
- Bài thuyết trình Promises and Generators: control flow utopia bởi Forbes Lindesay tại JSConf (2013)
- Task.js trên GitHub
- You Don't Know JS: Async & Performance, Ch.4: Generators bởi Kyle Simpson