Generator函數是ES6提供的一種異步編程解決方案,最大的特色就是 : 能夠交出函數的執行權 (即暫停執行)。編程
形式上 , Generator函數是一個普通函數 , 可是有兩個特徵 :異步
ES6沒有規定,function關鍵字與函數名之間的星號,寫在哪一個位置。這致使下面的寫法都能經過。異步編程
function * foo(x, y) { ··· }
function *foo(x, y) { ··· }
function* foo(x, y) { ··· }
function*foo(x, y) { ··· }
複製代碼
函數表達式函數
let foo = function * (){ ... }
複製代碼
函數調用spa
Generator 函數的調用方法與普通函數同樣,也是在函數名後面加上一對圓括號。不一樣的是,調用Generator函數後,該函數並不執行,返回的也不是函數運行結果,而是一個指向內部狀態的指針對象===》遍歷器對象(Iterator Object)。prototype
下一步,必須調用遍歷器對象的next方法,使得指針移向下一個狀態。也就是說,每次調用next方法,內部指針就從"函數頭部或上一次停下來的地方"開始執行,直到遇到下一個yield語句(或return語句)爲止。指針
function * fn(){
yield "one";
yield "two";
return "last";
}
let e = fn();
console.log(e.next()); // {value: "one", done: false}
console.log(e.next()); // {value: "two", done: false}
console.log(e.next()); // {value: "last", done: true}
console.log(e.next()); // {value: undefined, done: true}
複製代碼
value屬性表示當前的內部狀態的值,是yield語句後面那個表達式的值;done屬性是一個布爾值,表示是否遍歷結束。code
yield語句就是暫停標誌對象
遍歷器對象的next方法的運行邏輯以下:接口
注意 :
Generator函數能夠不用yield語句,這時就變成了一個單純的暫緩執行函數。
function * fn(){
console.log("hahaha")
}
let generator = fn();
setTimeout(function(){
generator.next()
},2000)
複製代碼
yield語句不能用在普通函數中,不然會報錯。
yield句自己沒有返回值,或者說老是返回undefined。next方法能夠帶一個參數,該參數就會被看成上一個yield語句的返回值。
注意了 : 是上一個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 }
// 第三步的true會當作上一個yield語句的返回值 , 也就是說 i會先賦值爲 -1 ,而後 i++ 變爲 0 輸出。
複製代碼
for...of循環能夠自動遍歷Generator函數時生成的Iterator對象,且此時再也不須要調用next方法。
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
複製代碼
一旦next方法的返回對象的done屬性爲true,for...of循環就會停止,且不包含該返回對象,因此上面代碼的return語句返回的6,不包括在for...of循環之中。可是 , 若將 6 換爲yield 則會輸出 , 由於此時 done 爲 false。
除了for...of循環之外,擴展運算符(...)、解構賦值和Array.from方法內部調用的,都是遍歷器接口。這意味着,它們均可以將Generator函數返回的Iterator對象,做爲參數。
// 擴展運算符
[...fn()] // [1,2,3,4,5]
// Array.from() 方法
Array.from(fn()) // [1,2,3,4,5]
// 解構賦值
let [x,y,z] = fn()
// x 1 y 2 z3
複製代碼
Generator函數返回的遍歷器對象,都有一個throw方法,能夠在函數體外拋出錯誤,而後在Generator函數體內捕獲。
function * fn(){
try {
yield
}catch(e){
console.log('內部捕獲',e.message)
}
}
var i = fn()
i.next()
try{
i.throw(new Error('你說你錯了'))
i.throw('不走心那')
}catch(e){
console.log("外部捕獲",e)
}
// 內部捕獲 你說你錯了
// 外部捕獲 不走心那
複製代碼
Generator函數返回的遍歷器對象,還有一個return方法,能夠返回給定的值,而且終結遍歷Generator函數。
function * fn(){
yield 1;
yield 2;
yield 3;
}
let g = fn();
console.log(g.next()) // {value: 1, done: false}
console.log(g.return(10)) //{value: 10, done: true}
console.log(g.next()) // {value: undefined, done: true}
複製代碼
若是return方法調用時,不提供參數,則返回值的value屬性爲undefined。
若是在Generater函數內部,調用另外一個Generator函數,默認狀況下是沒有效果的。 這個就須要用到yield*語句,用來在一個Generator函數裏面執行另外一個Generator函數。
function* foo() {
yield 'a';
yield 'b';
}
function* bar() {
yield 'x';
foo();
yield 'y';
}
function* bar() {
yield 'x';
yield* foo();
yield 'y';
}
// "x"
// "a"
// "b"
// "y"
複製代碼