generator yield-delegation

在generator函數內部,經過yield*語句,能夠將yield委託給其餘任何實現iterable的對象。javascript

委託給generator函數生成的iterable對象

調用generator函數會返回一個實現iterable的對象(該對象同時也是一個iterator)。java

經過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隱式returnundefined,而是返回了"bar 3"ide

若是foo內有顯式的return語句,那麼在進行yield-delegation時是否會返回foo return的值嗎?函數

下面的代碼在foo中添加了一個return語句。在bar內將yield* foo()表達式的值賦值給tmp變量並打印。ui

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方法的返回值。this

委託給其餘任何實現iterable的對象

generator內部的yield*語句也能將yield委託給其餘非generator生成的iterable對象。例如 數組就是一個實現了iterable的對象。url

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又是一個iteratorspa

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》內的例子。code

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);
複製代碼
相關文章
相關標籤/搜索