本系列屬於阮一峯老師所著的ECMAScript 6 入門學習筆記javascript
Generator函數是ES6提供的一種異步編程解決方案。形式上,Generator函數是一個普通函數,可是有兩個特徵:function
關鍵字與函數名之間有一個星號;函數體內部使用yield
表達式,定義不一樣的內部狀態(yield
表意產出)。java
function* helloWorldGenerator(){ yield 'hello' yield 'world' return 'ending' } var hw = helloWorldGenerator() // Generator函數和普通函數同樣調用,可是調用後函數並不執行,返回一個指向內部狀態的指針對象,即遍歷器對象(Iterator Object) hw.next() // {value: 'hello',done:false} hw.next() // {value: 'world',done:false} hw.next() // {value: 'ending',done:true} hw.next() // {value: undefined,done:true} // 每次調用next方法,執行Generator函數,依次返回yeild表達式的值,執行到return語句(若沒有return語句就執行到結束)
Generator函數返回遍歷器對象,只有調用next
方法纔會遍歷下一個狀態,yield
表達式就是暫停函數執行的暫停標誌。es6
Generator函數能夠不用yield
表達式,這時就變成一個單純的暫緩執行函數編程
function* f(){ console.log('done!') } var generator = f() // 函數不會當即執行,只有調用next方法纔會執行 setTimeout(function(){ generator.next() },2000)
yield
表達式若是在另外一個表達式中,必須放在圓括號內數組
function* demo(){ console.log('Hello' + yield) // SyntaxError console.log('Hello' + (yield)) // OK } // yield表達式用做函數參數或放在賦值表達式的右邊,能夠不加括號 function* demo(){ foo(yield 'a',yield 'b') // OK let input = yield // OK }
任何一個對象的Symbol.iterator
方法,等於該對象的遍歷器生成函數,調用該函數會返回該對象的一個遍歷器對象。因爲Generator函數就是遍歷器生成函數,所以能夠把Generator函數賦值給Symbol.iterator
屬性,從而使得該對象具備iterator接口。數據結構
var myIterator = {} myIterator[Symbol.iterator] = function* (){ yield 1 yield 2 yield 3 } [...myIterator] // [1,2,3]
yield
表達式自己沒有返回值,或者說每次都返回undefined。next
方法能夠帶一個參數,該參數會被當作上一個yield
表達式的返回值異步
function* f() { for(var i = 0; true; i++) { var reset = yield i; if(reset) { i = -1; } } } var g = f(); g.next() // { value: 0, done: false } g.next() // { value: 1, done: false } g.next(true) // { value: 0, done: false } // 每次運行到yield表達式,變量reset總被賦值undefined,當next帶上參數以後,遍歷reset就被賦值爲true
for...of
循環能夠自動遍歷Generator函數生成的Iterator
對象,且不用再調用next
方法異步編程
function* foo(){ yield 1 yield 2 yield 3 yield 4 return 5 } for(let v of foo()){ console.log(v) } // 1 2 3 4 // for...of循環遇到next方法返回對象的done爲true就會停止,因此return返回的5不在循環之中 // 除了for...of循環外,拓展運算符...、解構賦值和Array.from均可以將Generator函數返回的Iterator對象做爲參數 function* numbers () { yield 1 yield 2 return 3 yield 4 } // 拓展運算符 [...numbers()] // [1,2] // Array.from Array.from(numbers()) // [1,2] // 解構賦值 let [x,y] = numbers() x // 1 y // 2 // for...of循環 for(let n of numbers()){ console.log(n) // 1 2 }
Generator函數返回的遍歷器對象有一個throw
方法,能夠在函數體外拋出錯誤,而後在函數體內捕獲函數
var g = function* (){ try{ yield }catch(e){ console.log('內部捕獲',e) } } var i = g() i.next() // throw方法能夠接收一個參數,改參數會被catch語句接收,建議拋出Error對象實例 try { i.throw('a') i.throw('b') }catch(e){ console.log('外部捕獲',e) } // 內部捕獲 a // 外部捕獲 b // throw方法被捕獲後會附帶執行下一條yield表達式,也就是說會附帶執行一次next方法
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} g.next() // {value:undefined,done:true} // 若是Generator函數內部有try...finally代碼塊,那麼return方法會推遲到finally代碼執行完以後再執行 function* numbers(){ yield 1 try{ yield 2 yield 3 }finally{ yield 4 yield 5 } yield 6 } var g = numbers() g.next() // {value:1,done:false} g.next() // {value:2,done:false} g.return(7) // {value:4,done:false} g.next() // {value:5,done:false} g.next() // {value:7,done:true}
在Generator函數內調用另外一個Generator函數,默認狀況下是沒有效果的,這時候就要用到yield*
表達式
function* inner(){ yield 'hello!' } function* outer1(){ yield 'open' yield inner() yield 'close' } var gen = outer1() gen.next().value // 'open' gen.next().value // 返回一個遍歷器對象 gen.next().value // 'close' function* outer2(){ yield 'open' yield* inner() yield 'close' } var gen = outer2() gen.next().value // 'open' gen.next().value // 'hello' gen.next().value // ’close' function* concat(iter1,iter2){ yield* iter1 yield* iter2 } // 等同於 function* concat(iter1,iter2){ for(var value of iter1){ yield value } for(var value of iter2){ yield value } } function* gen(){ yield* ['a','b','c'] } gen().next() // {value:'a',done:false} // 若不加星號返回的是整個數組,加了就表示返回的是數組的遍歷器對象。實際上任何具備Iterator接口的數據結構均可以被yield遍歷 function *foo() { yield 2; yield 3; return "foo"; } function *bar() { yield 1; var v = yield *foo(); console.log( "v: " + v ); yield 4; } var it = bar(); it.next() // {value: 1, done: false} it.next() // {value: 2, done: false} it.next() // {value: 3, done: false} it.next(); // "v: foo" // {value: 4, done: false} it.next() // {value: undefined, done: true} //被代理的Generator函數foo有return語句,那麼就會向代理它的Generator函數bar返回數據,而且繼續執行next方法
let obj = { * myGeneratorMethod(){ ... } }
// 生成空對象,使用call方法綁定Generator函數內部的this function* F(){ this.a = 1 yield this.b = 2 yield this.c = 3 } var obj = {} var f = F.call(obj) // 調用三次next方法完成F內部全部代碼的運行,將全部內部屬性綁定在obj對象上 f.next() // {value:2,done:false} f.next() // {value:3,done:false} f.next() // {value:undefined,done:true} // obj對象編程了F的實例 obj.a // 1 obj.b // 2 obj.c // 3 // 將F改爲構造函數,能夠執行new命令 function* gen(){ this.a = 1 yield this.b = 2 yield this.c = 3 } function F(){ return gen.call(gen.prototype) } var f = new F() f.next() // {value:2,done:false} f.next() // {value:3,done:false} f.next() // {value:undefined,done:true} f.a // 1 f.b // 2 f.c // 3