// 使用 function* 定義一個 generator 函數 function* helloWorldGenerator() { yield 'hello'; // yield 關鍵字做爲暫停的點 yield 'world'; return 'ending'; } var hw = helloWorldGenerator(); // 執行 generator 函數,返回一個遍歷器對象。而這時,這個函數內的代碼並不會執行。
// 調用遍歷器對象的 next 方法,執行函數內的代碼,執行到下一個 yield 的位置,並暫停執行 hw.next() // { value: 'hello', done: false } value 是 yield 後面跟的表達式的值,done 是 genertator 函數結束狀態 // 再次調用 next,執行到下一個 yield 位置 hw.next() // { value: 'world', done: false } // 執行結束,value 值爲 return 的值,沒有 return 則爲 undefined(函數沒 return 返回 undefined),done 變爲 true hw.next() // { value: 'ending', done: true } // 還能夠無限次調用 next,可是都返回相同的對象 hw.next() // { value: undefined, done: true }
yield 不能用在普通函數中:數組
var flat = function* (a) { // forEach 方法是個普通函數,在裏面使用了 yield 會報錯。解決方法是改成 for 循環 a.forEach(function (item) { if (typeof item !== 'number') { yield* flat(item); } else { yield item; } } };
yield
語句若是用在一個表達式之中,必須放在圓括號裏面。函數
console.log('Hello' + yield); // SyntaxError console.log('Hello' + yield 123); // SyntaxError console.log('Hello' + (yield)); // OK console.log('Hello' + (yield 123)); // OK
next方法的參數this
function* foo(x) { var y = 2 * (yield (x + 1)); // yield 語句在表達式中,須要將 yield 語句括起來,不然報錯 var z = yield (y / 3); return (x + y + z); } var a = foo(5); a.next() // Object{value:6, done:false} a.next() // Object{value:NaN, done:false} a.next() // Object{value:NaN, done:true} var b = foo(5); b.next() // { value:6, done:false } 調用第一次 next 開始執行,獲得第一個 yield 的返回值 6。因爲 next 參數爲上一個 yield 語句的值,因此第一個 next 傳入參數沒有意義 b.next(12) // { value:8, done:false } 調用 next 方法時注入了數據,做爲上一個 yield 語句的值,獲得 var y = 2 * 12 b.next(13) // { value:42, done:true } 獲得 var z = 13
for...of循環spa
for...of
循環能夠自動遍歷Generator函數時生成的Iterator
對象,且此時再也不須要調用next
方法。prototype
function *foo() { yield 1; yield 2; yield 3; yield 4; yield 5; return 6; } for (let v of foo()) { console.log(v); } // 1 2 3 4 5 這裏須要注意,一旦方法的返回對象的屬性爲,循環就會停止,且不包含該返回對象,因此上面代碼的語句返回的6,不包括在循環之中。nextdonetruefor...ofreturnfor...of
原生的JavaScript對象沒有遍歷接口,沒法使用for...of
循環,經過Generator函數爲它加上這個接口,就能夠用了。code
// 第一種方法 function* objectEntries(obj) { let propKeys = Reflect.ownKeys(obj); for (let propKey of propKeys) { yield [propKey, obj[propKey]]; } } let jane = { first: 'Jane', last: 'Doe' }; for (let [key, value] of objectEntries(jane)) { console.log(`${key}: ${value}`); } // 第二種方法 function* objectEntries() { let propKeys = Object.keys(this); for (let propKey of propKeys) { yield [propKey, this[propKey]]; } } let jane = { first: 'Jane', last: 'Doe' }; jane[Symbol.iterator] = objectEntries; for (let [key, value] of jane) { console.log(`${key}: ${value}`); }
Generator.prototype.throw()對象
Generator函數返回的遍歷器對象,都有一個throw
方法,能夠在函數體外拋出錯誤,而後在Generator函數體內捕獲。blog
var g = function* () {
// 使用 try...catch... 進行異常捕獲
try { yield; } catch (e) { console.log('內部捕獲', e); } }; var i = g(); i.next(); try { i.throw('a'); // 這裏使用 throw 方法拋出的錯誤,會由 generator 函數內的 catch 處理 i.throw('b'); // generator 內的 catch 已經執行過了,就不會再被 generator 的 catch 捕獲了,由外部的 catch 捕獲 } catch (e) { console.log('外部捕獲', e); } // 內部捕獲 a // 外部捕獲 b
若是Generator函數內部沒有部署try...catch
代碼塊,那麼throw
方法拋出的錯誤,將被外部try...catch
代碼塊捕獲。接口
若是Generator函數內部和外部,都沒有部署try...catch
代碼塊,那麼程序將報錯,直接中斷執行。ip
throw
方法被捕獲之後,會附帶執行下一條yield
語句。也就是說,會附帶執行一次next
方法。
var gen = function* gen(){ try { yield console.log('a'); } catch (e) { // ... } yield console.log('b'); // throw 方法會附帶執行 next,從而執行到這個 yield 位置 yield console.log('c'); } var g = gen(); g.next() // a g.throw() // b g.next() // c
Generator.prototype.return()
Generator函數返回的遍歷器對象,還有一個return
方法,能夠返回給定的值,而且終結遍歷Generator函數。
function* gen() { yield 1; yield 2; yield 3; } var g = gen(); g.next() // { value: 1, done: false } g.return('foo') // { value: "foo", done: true } value 值變成了 return 的參數 g.next() // { value: undefined, done: true } return 方法 致使 generator 函數結束,因此 value 爲 undefined
yield*語句
function* foo() { yield 'a'; yield 'b'; } function* bar() { yield 'x'; // foo(); 若是隻是單純的執行 foo() 函數,只是獲得一個遍歷器對象,並不會產生什麼效果。 yield* foo(); // 使用了 yield* 語句,在遍歷的時候纔會遍歷這個 generator 函數內部的 generator 函數。 yield 'y'; } for (let v of bar()){ console.log(v); } // "x" // "a" // "b" // "y"
function* gen(){ yield* ["a", "b", "c"]; // 數組、字符串等,帶有 iterator 接口的,均可以被 yield* 遍歷 } gen().next() // { value:"a", done:false }
Generator與狀態機
var clock = function*() { while (true) { console.log('Tick!'); // 執行狀態1代碼 yield; console.log('Tock!'); // 執行狀態2代碼 yield; } };
每次調用 next() 就能夠在兩種狀態間切換執行,而不須要使用一個布爾變量來作判斷