在generator函式內部,通過yield*
語句,可以將yield
委託給其他任何實現iterable
的物件。
委託給generator函式生成的iterable
物件
呼叫generator函式會返回一個實現iterable
的物件(該物件同時也是一個iterator
)。
通過yield* otherGenerator()
可以將generator內的yield
委託給其他generator生成的iterable
物件。
function* foo() {
console.log('*foo() starting');
yield 'foo 1';
yield 'foo 2';
console.log('*foo() finished');
}
function* bar() {
yield 'bar 1';
yield 'bar 2';
yield* foo(); // `yield`-delegation!
yield 'bar 3';
}
var it = bar();
it.next().value;
// "bar 1"
it.next().value;
// "bar 2"
it.next().value;
// *foo() starting
// "foo 1"
it.next().value;
// "foo 2"
it.next().value;
// *foo() finished
// "bar 3"
複製程式碼
可以看到上面的程式碼中,在呼叫第3個next
方法時返回的是foo
裡面yield
的"foo 1"
;在呼叫第5個next
時,並沒有返回foo
generator隱式return
的undefined
,而是返回了"bar 3"
。
如果foo
內有顯式的return
語句,那麼在進行yield-delegation時是否會返回foo
return
的值嗎?
下面的程式碼在foo
中新增了一個return
語句。在bar
內將yield* foo()
表示式的值賦值給tmp
變數並列印。
function* foo() {
console.log('*foo() starting');
yield 'foo 1';
yield 'foo 2';
console.log('*foo() finished');
return 'foo 3'
}
function* bar() {
yield 'bar 1';
yield 'bar 2';
var tmp = yield* foo(); // `yield`-delegation!
console.log('在bar內列印', tmp);
yield 'bar 3';
}
var it = bar();
it.next().value;
// "bar 1"
it.next().value;
// "bar 2"
it.next().value;
// *foo() starting
// "foo 1"
it.next().value;
// "foo 2"
it.next().value;
// *foo() finished
// 在bar內列印 foo 3
// "bar 3"
複製程式碼
在第5次呼叫next
方法時,可以發現foo
return
的"foo 3"
變成了yield* foo()
表示式的值,並被列印為"在bar內列印 foo 3"
;"bar 3"
成為next
方法的返回值。
委託給其他任何實現iterable
的物件
generator內部的yield*
語句也能將yield
委託給其他非generator生成的iterable
物件。例如 陣列就是一個實現了iterable
的物件。
function* bar() {
console.log("inside `*bar()`:", yield "A");
// `yield`-delegation to a non-generator!
console.log("inside `*bar()`:", yield* ["B", "C", "D"]);
console.log("inside `*bar()`:", yield "E");
return "F";
}
var it = bar();
console.log("outside:", it.next().value);
// outside: A
console.log("outside:", it.next(1).value);
// inside `*bar()`: 1
// outside: B
console.log("outside:", it.next(2).value);
// outside: C
console.log("outside:", it.next(3).value);
// outside: D
console.log("outside:", it.next(4).value);
// inside `*bar()`: undefined
// outside: E
console.log("outside:", it.next(5).value);
// inside `*bar()`: 5
// outside: F
複製程式碼
也可以委託給自己手寫的iterable
物件。由於javascript不是強型別語言,如果物件上含有Symbol.iterator
方法,那麼就可以將該物件當做一個iterable
物件;如果物件上含有next
方法,那就可以將該物件當做一個iterator
物件。下面的myIterable
物件即實現了Symbol.iterator
方法也實現了next
方法,所以它即是一個iterable
又是一個iterator
。
var myIterable = {
[Symbol.iterator]: function () {
return this;
},
num: 98,
next: function () {
var self = this;
if (self.num < 101) {
return { value: String.fromCharCode(self.num++), done: false };
} else {
return { value: String.fromCharCode(101), done: true };
}
}
}
function* bar() {
console.log("inside `*bar()`:", yield "A");
// `yield`-delegation to a non-generator!
console.log("inside `*bar()`:", yield* myIterable);
console.log("inside `*bar()`:", yield "E");
return "F";
}
var it = bar();
console.log("outside:", it.next().value);
// outside: A
console.log("outside:", it.next(1).value);
// inside `*bar()`: 1
// outside: b
console.log("outside:", it.next(2).value);
// outside: c
console.log("outside:", it.next(3).value);
// outside: d
console.log("outside:", it.next(4).value);
// inside `*bar()`: e
// outside: E
console.log("outside:", it.next(5).value);
// inside `*bar()`: 5
// outside: F
複製程式碼
異常委託
被委託的iterator
內部執行過程發生異常,如果異常被捕獲,那麼捕獲處理完異常後還按原來的邏輯執行;如果異常未被捕獲,那麼異常會被丟擲,異常會被拋到yield*
語句那。下面是《YOU DON'T KNOW JS》內的例子。
function* foo() {
try {
yield "B";
}
catch (err) {
console.log("error caught inside `*foo()`:", err);
}
yield "C";
throw "D";
}
function* bar() {
yield "A";
try {
yield* foo();
}
catch (err) {
console.log("error caught inside `*bar()`:", err);
}
yield "E";
yield* baz();
// note: can't get here!
yield "G";
}
function* baz() {
throw "F";
}
var it = bar();
console.log("outside:", it.next().value);
// outside: A
console.log("outside:", it.next(1).value);
// outside: B
console.log("outside:", it.throw(2).value);
// error caught inside `*foo()`: 2
// outside: C
console.log("outside:", it.next(3).value);
// error caught inside `*bar()`: D
// outside: E
try {
console.log("outside:", it.next(4).value);
}
catch (err) {
console.log("error caught outside:", err);
}
// error caught outside: F
複製程式碼
遞迴委託
下面是《YOU DON'T KNOW JS》裡的例子。
function* foo(val) {
if (val > 1) {
// generator recursion
val = yield* foo(val - 1);
}
return yield request("http://some.url/?v=" + val);
}
function* bar() {
var r1 = yield* foo(3);
console.log(r1);
}
run(bar);
複製程式碼