初遇
生成器函數(shù),顧名思義,生成對象,生成一個遍歷器。
function* generator(){
yield 0;
yield 1;
yield 2;
}
let iterator = generator();
iterator.next(); //{value:0,done:false}
iterator.next(); //{value:1,done:false}
iterator.next(); //{value:2,done:false}
iterator.next(); //{value:undefined,done:true}
-
generator()為生成器函數(shù),在function和函數(shù)名之間添加*表明該函數(shù)為生成器函數(shù)。 -
generaotor()調(diào)用返回一個遍歷器,賦值給iterator。 -
iterator有多個方法,next()、return()、throw()。 - 通過
yield和上述三個方法控制生成器函數(shù)代碼塊的執(zhí)行。
yield和next( )
- 生成器代碼不會自動執(zhí)行,多個yield將代碼分成多個部分,等待信號才會執(zhí)行,信號就是上述遍歷器的幾個方法。
- 調(diào)用
next()時生成器代碼執(zhí)行,執(zhí)行完含yield語句時停止。下一次調(diào)用next()時,繼續(xù)執(zhí)行。 -
next()的返回值為一個對象:{value:"",done:false},該對象包含value、done字段。 -
value值為本次執(zhí)行代碼中yield其后表達式的值。done的值為true或者false,表示當前遍歷器是否執(zhí)行完畢。 - 上述代碼執(zhí)行情況
1.第一個next()啟動生成器執(zhí)行,執(zhí)行完yield 0后停止,next()返回值為{value:0,done:false}。
2.第二個next()調(diào)用,生成器繼續(xù)執(zhí)行,執(zhí)行完yield 1后停止,next()返回值為{value:1,done:false}。
3.第三次next()調(diào)用和第二次相似,next()返回值為{value:2,done:false}
4.第四次next()調(diào)用時,生成器已執(zhí)行完畢,value字段值默認值undefined,done字段值為true。后續(xù)調(diào)用都是如此。yield語句返回值、next( )參數(shù)
-
yield語句默認為值為undefined。
function* generator () { const data0 = yield 0; console.log(data0); //打印出值為undefined const data1 = yield 1; console.log(data1); //打印出值為undefined const data2 = yield 2; console.log(data2); //打印出值為undefined } let iterator = generator(); iterator.next(); iterator.next(); iterator.next(); iterator.next(); //yield語句默認值為undefined,data0、data1、data2所賦到的值為undefined- 通過向
next()傳入?yún)?shù)改變上個yield語句的返回值。
function* generator () { const data0 = yield 1; console.log(data0); //打印出值為"data0" const data1 = yield 1; console.log(data1); //打印出值為"data1" const data2 = yield 2; console.log(data2); //打印出值為"data2" } let iterator = generator(); iterator.next(); iterator.next("data0"); iterator.next("data1"); iterator.next("data2"); -
return( )
這樣一段代碼,生成器中含return語句。遍歷器剛好結(jié)束遍歷時,value字段默認值為undefined,return返回的值會賦給當前next()返回值的value字段。即代碼塊中第三次next()調(diào)用。
function* generator(){
yield 0;
yield 1;
return 2;
}
let iterator = generator();
console.log(iterator.next()); //{value:0,done:false}
console.log(iterator.next()); //{value:1,done:false}
console.log(iterator.next()); //{value:2,done:true}
console.log(iterator.next()); //{value:undefined,done:true}
遍歷器的return()
遍歷器的next()表示繼續(xù)向后執(zhí)行,return則表示直接返回,結(jié)束執(zhí)行。
function* generator() {
yield 0;
yield 1;
yield 2;
}
let iterator = generator();
console.log(iterator.next());
console.log(iterator.return());
- 上述代碼執(zhí)行完
yield 0后停止,return()被調(diào)用,提前結(jié)束遍歷。后續(xù)yield 1、yield 2不會被執(zhí)行。 -
return()返回值默認為{value:undefined,done:true}。value字段值可由return()的參數(shù)設置,同next()。
throw( )
- 所生成遍歷器也有自己的
throw(),會優(yōu)先觸發(fā)生成器內(nèi)部的try-catch。
function* generator() {
try {
const data = yield;
} catch(e) {
console.log("內(nèi)部檢測到",e.message);
}
}
let iterator = generator();
iterator.next();
iterator.throw(new Error("error"))
- 內(nèi)部無
try-catch時,觸發(fā)外部try-catch。 - 多次
throw()時,第一次由內(nèi)部捕獲,后續(xù)由外部捕獲。
底層原理
- 基于協(xié)程的實現(xiàn)。
- 單個線程上可有多個協(xié)程,線程控制權(quán)可交由不同的協(xié)程控制,每個時間點只能由一個協(xié)程控制。
- 運行生成器函數(shù)時創(chuàng)建新的協(xié)程,所創(chuàng)建它的協(xié)程的稱為父協(xié)程。
- 開始時由父協(xié)程控制,調(diào)用
next()時,將線程控制權(quán)交由生成器協(xié)程,遇到yeild時將控制權(quán)交回父協(xié)程。 - 每個協(xié)程在轉(zhuǎn)交控制權(quán)前都會保存自己的調(diào)用棧,方便再次執(zhí)行。
本文只是浮光掠影,更多細節(jié)內(nèi)容請前往阮一峰ES6