Generator(三)

Iterator高級功能

給迭代器傳遞參數(shù)

可以通過next()方法給迭代器傳遞參數(shù)。當(dāng)通過next()傳遞參數(shù)時,參數(shù)變成了yield語句在生成器中的值。這個能力對很多高級功能非常重要,例如異步編程。例子

function *createIterator() {
    let first = yield 1;
    let second = yield first + 2; // 4 + 2
    yield second + 3; // 5 + 3
}
let iterator = createIterator();
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next(4)); // "{ value: 6, done: false }"
console.log(iterator.next(5)); // "{ value: 8, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"

首次調(diào)用next()比較特殊,傳遞任何參數(shù)都會丟失。此處第一次調(diào)用next(),執(zhí)行到y(tǒng)ield
1,輸出1,然后停止執(zhí)行;第二次調(diào)用next(4),參數(shù)為4,繼續(xù)生成器的執(zhí)行,4作為yield 1的值被賦給first,然后執(zhí)行到y(tǒng)ield first + 2,輸出6,停止執(zhí)行。整個執(zhí)行過程如下圖所示:

生成器中的代碼執(zhí)行過程

其中淺灰區(qū)域為第一次調(diào)用next執(zhí)行的代碼,中灰區(qū)域為第二次調(diào)用next執(zhí)行的代碼,深灰區(qū)域為第三次調(diào)用next執(zhí)行的代碼

在迭代器中拋出錯誤

除了可以給迭代器傳遞數(shù)據(jù)之外,還可以傳遞錯誤條件。迭代器實現(xiàn)了一個throw方法,可以在重新開始時讓迭代器拋出錯誤。這對異步編程是一個很重要的能力,也提高了生成器內(nèi)部的靈活性(可以像普通函數(shù)一樣返回值和拋出錯誤)。例子:

function *createIterator() {
    let first = yield 1;
    let second = yield first + 2; // yield 4 + 2, then throw
    yield second + 3; // never is executed
}
let iterator = createIterator();
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next(4)); // "{ value: 6, done: false }"
console.log(iterator.throw(new Error("Boom"))); // error thrown from generator
生成器中拋出錯誤

了解了這一點就可以在生成器中使用try-catch捕獲這類錯誤,例子:

function *createIterator() {
  let first = yield 1;
  let second;
  try {
    second = yield first + 2; // yield 4 + 2, then throw
  } catch (ex) {
    second = 6; // on error, assign a different value
  }
  yield second + 3;
}
let iterator = createIterator();
console.log(iterator.next()); // "{ value: 1, done: false }"
console.log(iterator.next(4)); // "{ value: 6, done: false }"
console.log(iterator.throw(new Error("Boom"))); // "{ value: 9, done: false }"
console.log(iterator.next()); // "{ value: undefined, done: true }"

生成器返回語句

生成器也是函數(shù),所以也可以使用return語句提前退出或者給next方法的最后一次調(diào)用指定一個返回值。大多數(shù)情況下,Iterator上最后一次next調(diào)用返回undefined,但你可以利用return指定一個值。
例子1:

function *createIterator() {
    yield 1;
    return;
    yield 2;
    yield 3;
}
let iterator = createIterator();
console.log(iterator.next());           // "{ value: 1, done: false }"
console.log(iterator.next());           // "{ value: undefined, done: true }"

例子2:

function *createIterator() {
    yield 1;
    return 42;
}
let iterator = createIterator();
console.log(iterator.next());           // "{ value: 1, done: false }"
console.log(iterator.next());           // "{ value: 42, done: true }"
console.log(iterator.next());           // "{ value: undefined, done: true }"

注意:Spread Operator和for of循環(huán)會忽略return的返回值。它們只要確認(rèn)done為true,就不再讀取value的值。但是在委托生成器時return返回的值是有用的。

委托生成器(Delegating Generators)

在一些情況下,合并兩個生成器的內(nèi)容到一起是有用的。生成器可以使用一種特殊形式的yield(后面帶星號)委托到其他生成器。和生成器定義一樣,這個星號只要在yield和生成器函數(shù)之間即可。例子:

function *createNumberIterator() {
    yield 1;
    yield 2;
}
function *createColorIterator() {
    yield "red";
    yield "green";
}
function *createCombinedIterator() {
    yield *createNumberIterator();
    yield *createColorIterator();
    yield true;
}
var iterator = createCombinedIterator();
console.log(iterator.next());           // "{ value: 1, done: false }"
console.log(iterator.next());           // "{ value: 2, done: false }"
console.log(iterator.next());           // "{ value: "red", done: false }"
console.log(iterator.next());           // "{ value: "green", done: false }"
console.log(iterator.next());           // "{ value: true, done: false }"
console.log(iterator.next());           // "{ value: undefined, done: true }"

生成器委托也可以讓我們充分利用return的返回值。例子:

function *createNumberIterator() {
    yield 1;
    yield 2;
    return 3;
}
function *createRepeatingIterator(count) {
    for (let i=0; i < count; i++) {
        yield "repeat";
    }
}
function *createCombinedIterator() {
    let result = yield *createNumberIterator();
    yield *createRepeatingIterator(result);
}
var iterator = createCombinedIterator();
console.log(iterator.next());           // "{ value: 1, done: false }"
console.log(iterator.next());           // "{ value: 2, done: false }"
console.log(iterator.next());           // "{ value: "repeat", done: false }"
console.log(iterator.next());           // "{ value: "repeat", done: false }"
console.log(iterator.next());           // "{ value: "repeat", done: false }"
console.log(iterator.next());           // "{ value: undefined, done: true }"

異步任務(wù)運行

生成器最令人興奮的地方適合異步編程有關(guān)的。它能夠讓代碼在執(zhí)行過程中暫停,這為異步編程打開了一扇新的大門,讓我們不必糾纏于多層嵌套的回調(diào)當(dāng)中。
傳統(tǒng)的回調(diào)方式處理異步編程:

let fs = require("fs");
fs.readFile("config.json", function(err, contents) {
    if (err) {
        throw err;
    }
    doSomethingWith(contents);
    console.log("Done");
});

簡單的任務(wù)運行器

function run(taskDef) {
    // create the iterator, make available elsewhere
    let task = taskDef();
    // start the task
    let result = task.next();
    // recursive function to keep calling next()
    function step() {
        // if there's more to do
        if (!result.done) {
            result = task.next();
            step();
        }
    }
    // start the process
    step();
}
run(function*() {
    console.log(1);
    yield;
    console.log(2);
    yield;
    console.log(3);
});

帶數(shù)據(jù)的任務(wù)運行器

function run(taskDef) {
    // create the iterator, make available elsewhere
    let task = taskDef();
    // start the task
    let result = task.next();
    // recursive function to keep calling next()
    function step() {
        // if there's more to do
        if (!result.done) {
            result = task.next(result.value);
            step();
        }
    }
    // start the process
    step();
}
run(function*() {
    let value = yield 1;
    console.log(value);         // 1
    value = yield value + 3;
    console.log(value);         // 4
});

異步任務(wù)運行器

let fs = require("fs");

function run(taskDef) {
  // create the iterator, make available elsewhere
  let task = taskDef();
  // start the task
  let result = task.next();
  // recursive function to keep calling next()
  function step() {
    // if there's more to do
    if (!result.done) {
      if (typeof result.value === "function") {
        result.value(function(err, data) {
          if (err) {
            result = task.throw(err);
            return;
          }
          result = task.next(data);
          step();
        });
      } else {
        result = task.next(result.value);
        step();
      }
    }
  }
  // start the process
  step();
}

function readFile(filename) {
  return function(callback) {
    fs.readFile(filename, callback);
  };
}

run(function*() {
  let contents = yield readFile("config.json");
  doSomethingWith(contents);
  console.log("Done");
});
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容