yield*
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.
Toán tử yield* có thể được sử dụng trong các hàm generator (đồng bộ hoặc bất đồng bộ) để ủy quyền cho một đối tượng iterable khác, như Generator. Trong các hàm async generator, nó còn có thể được sử dụng để ủy quyền cho một đối tượng async iterable khác, như AsyncGenerator.
Try it
function* func1() {
yield 42;
}
function* func2() {
yield* func1();
}
const iterator = func2();
console.log(iterator.next().value);
// Expected output: 42
Cú pháp
yield* expression
Tham số
expressionOptional-
Một đối tượng iterable.
Giá trị trả về
Trả về giá trị được trả bởi iterator đó khi nó được đóng (khi done là true).
Mô tả
Biểu thức yield* duyệt qua toán hạng và yield từng giá trị được trả bởi nó. Nó ủy quyền việc duyệt qua của generator hiện tại cho một iterator cơ bản — mà chúng ta sẽ gọi tương ứng là "generator" và "iterator". yield* đầu tiên lấy iterator từ toán hạng bằng cách gọi phương thức [Symbol.iterator]() của nó. Sau đó, mỗi lần phương thức next() của generator được gọi, yield* gọi phương thức next() của iterator, truyền đối số nhận được bởi phương thức next() của generator (luôn là undefined cho lần gọi đầu tiên), và yield cùng một result object như những gì được trả về từ phương thức next() của iterator. Nếu iterator result có done: true, thì biểu thức yield* dừng thực thi và trả về value của result đó.
Toán tử yield* cũng chuyển tiếp các phương thức throw() và return() của generator hiện tại đến iterator cơ bản. Nếu generator hiện tại bị đóng sớm thông qua một trong các phương thức này, iterator cơ bản sẽ được thông báo. Nếu phương thức throw()/return() của generator được gọi, phương thức throw()/return() của iterator cơ bản sẽ được gọi với cùng đối số. Giá trị trả về của throw()/return() được xử lý như kết quả của phương thức next(), và nếu phương thức ném ra lỗi, ngoại lệ được lan truyền từ biểu thức yield*.
Nếu iterator cơ bản không có phương thức return(), biểu thức yield* trở thành câu lệnh return, giống như gọi return() trên biểu thức yield đang bị treo.
Nếu iterator cơ bản không có phương thức throw(), điều này khiến yield* ném ra TypeError — nhưng trước khi ném lỗi, phương thức return() của iterator cơ bản sẽ được gọi nếu có.
Ví dụ
>Ủy quyền cho generator khác
Trong code sau, các giá trị được yield bởi g1() được trả về từ các lần gọi next() giống như những giá trị được yield bởi g2().
function* g1() {
yield 2;
yield 3;
yield 4;
}
function* g2() {
yield 1;
yield* g1();
yield 5;
}
const gen = g2();
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: 4, done: false}
console.log(gen.next()); // {value: 5, done: false}
console.log(gen.next()); // {value: undefined, done: true}
Các đối tượng iterable khác
Ngoài các đối tượng generator, yield* cũng có thể yield các loại iterable khác (ví dụ: mảng, chuỗi, hoặc đối tượng arguments).
function* g3(...args) {
yield* [1, 2];
yield* "34";
yield* args;
}
const gen = g3(5, 6);
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: "4", done: false}
console.log(gen.next()); // {value: 5, done: false}
console.log(gen.next()); // {value: 6, done: false}
console.log(gen.next()); // {value: undefined, done: true}
Giá trị của chính biểu thức yield*
yield* là một biểu thức, không phải câu lệnh, vì vậy nó được đánh giá thành một giá trị.
function* g4() {
yield* [1, 2, 3];
return "foo";
}
function* g5() {
const g4ReturnValue = yield* g4();
console.log(g4ReturnValue); // 'foo'
return g4ReturnValue;
}
const gen = g5();
console.log(gen.next()); // {value: 1, done: false}
console.log(gen.next()); // {value: 2, done: false}
console.log(gen.next()); // {value: 3, done: false} done is false because g5 generator isn't finished, only g4
console.log(gen.next()); // {value: 'foo', done: true}
Sử dụng với async generator
async function* g1() {
await Promise.resolve(0);
yield "foo";
}
function* g2() {
yield "bar";
}
async function* g3() {
// Can use yield* on both async and sync iterators
yield* g1();
yield* g2();
}
const gen = g3();
console.log(await gen.next()); // {value: "foo", done: false}
console.log(await gen.next()); // {value: "bar", done: false}
console.log(await gen.next()); // {done: true}
Chuyển tiếp phương thức
Các phương thức next(), throw(), và return() của generator hiện tại đều được chuyển tiếp đến iterator cơ bản.
const iterable = {
[Symbol.iterator]() {
let count = 0;
return {
next(v) {
console.log("next called with", v);
count++;
return { value: count, done: false };
},
return(v) {
console.log("return called with", v);
return { value: "iterable return value", done: true };
},
throw(v) {
console.log("throw called with", v);
return { value: "iterable thrown value", done: true };
},
};
},
};
function* gf() {
yield* iterable;
return "gf return value";
}
const gen = gf();
console.log(gen.next(10));
// next called with undefined; the argument of the first next() call is always ignored
// { value: 1, done: false }
console.log(gen.next(20));
// next called with 20
// { value: 2, done: false }
console.log(gen.return(30));
// return called with 30
// { value: 'iterable return value', done: true }
console.log(gen.next(40));
// { value: undefined, done: true }; gen is already closed
const gen2 = gf();
console.log(gen2.next(10));
// next called with undefined
// { value: 1, done: false }
console.log(gen2.throw(50));
// throw called with 50
// { value: 'gf return value', done: true }
console.log(gen.next(60));
// { value: undefined, done: true }; gen is already closed
Nếu phương thức return()/throw() của iterator cơ bản trả về done: false, generator hiện tại tiếp tục thực thi và yield* tiếp tục ủy quyền cho iterator cơ bản.
const iterable = {
[Symbol.iterator]() {
let count = 0;
return {
next(v) {
console.log("next called with", v);
count++;
return { value: count, done: false };
},
return(v) {
console.log("return called with", v);
return { value: "iterable return value", done: false };
},
};
},
};
function* gf() {
yield* iterable;
return "gf return value";
}
const gen = gf();
console.log(gen.next(10));
// next called with undefined
// { value: 1, done: false }
console.log(gen.return(20));
// return called with 20
// { value: 'iterable return value', done: false }
console.log(gen.next(30));
// { value: 2, done: false }; gen is not closed
Nếu iterator cơ bản không có phương thức throw() và throw() của generator được gọi, yield* sẽ ném ra lỗi.
const iterable = {
[Symbol.iterator]() {
let count = 0;
return {
next(v) {
count++;
return { value: count, done: false };
},
};
},
};
function* gf() {
yield* iterable;
return "gf return value";
}
const gen = gf();
gen.next(); // First next() starts the yield* expression
gen.throw(20); // TypeError: The iterator does not provide a 'throw' method.
Thông số kỹ thuật
| Specification |
|---|
| ECMAScript® 2027 Language Specification> # sec-generator-function-definitions-runtime-semantics-evaluation> |