generator函數(shù)
Generator 函數(shù)整體就是一個狀態(tài)機(jī),內(nèi)部有多少個yield,就有多少個狀態(tài)悠瞬,當(dāng)Generator函數(shù)執(zhí)行的時候 其實是返回包含每次代碼暫停執(zhí)行的指針對象(遍歷器對象Iterator)谜洽,可以遍歷 Generator的各種狀態(tài)(Iterator.next 去移動指針可以使 Generator 函數(shù)分段執(zhí)行)
{value:xxx,done:false || true},表示這個狀態(tài)的返回值和一個表示這個遍歷對象是否已經(jīng)遍歷到最后(狀態(tài)機(jī)是否走完了所有的狀態(tài))
Iterator接口
typescript的接口描述如下:遍歷器接口(Iterable)、指針對象(Iterator)和next方法返回值
interface Iterable {
[Symbol.iterator]() : Iterator,
}
interface Iterator {
next(value?: any) : IterationResult,
}
interface IterationResult {
value: any,
done: boolean,
}
一種數(shù)據(jù)結(jié)構(gòu)只要部署了 Iterator 接口檩小,我們就稱這種數(shù)據(jù)結(jié)構(gòu)是“可遍歷的”(iterable)。
ES6 規(guī)定烟勋,默認(rèn)的 Iterator 接口部署在數(shù)據(jù)結(jié)構(gòu)的Symbol.iterator屬性规求,或者說筐付,一個數(shù)據(jù)結(jié)構(gòu)只要具有Symbol.iterator屬性,就可以認(rèn)為是“可遍歷的”(iterable)
-ES6 的有些數(shù)據(jù)結(jié)構(gòu)原生具備 Iterator 接口(比如數(shù)組)阻肿,即不用任何處理家妆,就可以被for...of循環(huán)遍歷。原因在于冕茅,這些數(shù)據(jù)結(jié)構(gòu)原生部署了Symbol.iterator屬性(詳見下文)伤极,另外一些數(shù)據(jù)結(jié)構(gòu)沒有(比如對象)。凡是部署了Symbol.iterator屬性的數(shù)據(jù)結(jié)構(gòu)姨伤,就稱為部署了遍歷器接口哨坪。調(diào)用這個接口,就會返回一個遍歷器對象
調(diào)用 Iterator 接口的場合
- (1)解構(gòu)賦值 對數(shù)組和 Set 結(jié)構(gòu)進(jìn)行解構(gòu)賦值時乍楚,會默認(rèn)調(diào)用Symbol.iterator方法当编。
- (2)擴(kuò)展運(yùn)算符 擴(kuò)展運(yùn)算符(...)也會調(diào)用默認(rèn)的 Iterator 接口。
- (3)yield* yield*后面跟的是一個可遍歷的結(jié)構(gòu)徒溪,它會調(diào)用該結(jié)構(gòu)的遍歷器接口忿偷。
let generator = function* () {
yield 1;
yield* [2,3,4];
yield 5;
};
var iterator = generator();
iterator.next() // { value: 1, done: false }
iterator.next() // { value: 2, done: false }
iterator.next() // { value: 3, done: false }
iterator.next() // { value: 4, done: false }
iterator.next() // { value: 5, done: false }
iterator.next() // { value: undefined, done: true }
for...in循環(huán)有幾個缺點。
數(shù)組的鍵名是數(shù)字臊泌,但是for...in循環(huán)是以字符串作為鍵名“0”鲤桥、“1”、“2”等等渠概。
for...in循環(huán)不僅遍歷數(shù)字鍵名茶凳,還會遍歷手動添加的其他鍵,甚至包括原型鏈上的鍵播揪。
某些情況下贮喧,for...in循環(huán)會以任意順序遍歷鍵名。
總之猪狈,for...in循環(huán)主要是為遍歷對象而設(shè)計的箱沦,不適用于遍歷數(shù)組。
看了好幾遍阮一峰的es6語法才看懂
Generator 函數(shù)返回一個遍歷器 這個遍歷器提供 next() 方法來控制Generator函數(shù)的執(zhí)行雇庙,暫停 谓形。
yield 表達(dá)式 暫停的標(biāo)志
簡易版本的Generator函數(shù)遍歷器自執(zhí)行函數(shù):
// 協(xié)程 callback版本
var timeout = (time)=>{
return (callbck)=>{
setTimeout(()=>{
console.log('time',time)
callbck()
},time)
}
}
// Generator 遍歷器生成函數(shù)
function* callbackGenerator() {
var res = yield timeout(2000) // 使得 yield 的value 是一個callback 自執(zhí)行遞歸 使下一次的next在callback里面執(zhí)行
var res1 = yield timeout(2000)
}
// callback版本 版本自執(zhí)行函數(shù)
function run (Generator) {
var hw = Generator();
const next = ()=>{
const aa = hw.next()
if(!aa.done){
aa.value(next)
}else{
return true
}
}
next()
}
run(callbackGenerator)
// 協(xié)程 promise 版本
var timeout = (time)=>{
return new Promise((resolve)=>{
setTimeout(()=>{
console.log('time',time)
resolve(time+2000)
},time)
})
}
// Generator 遍歷起器生成函數(shù)
function* callbackGenerator() {
var res = yield timeout(2000) // 使得 yield 的value是promise 自執(zhí)行時把下一次的next在then里面執(zhí)行
var res1 = yield timeout(res)
}
// promise版本 版本自執(zhí)行函數(shù)
function run (Generator) {
var hw = Generator();
const next = (query)=>{
var aa = hw.next(query)
if(!aa.done){
aa.value.then(next)
}else{
return true
}
}
next()
}
run(callbackGenerator)
核心思想:利用generator遍歷器生成器函數(shù)的分段執(zhí)行 ,只有在遍歷器對象 執(zhí)行next方法之后交出了控制權(quán) 状共,在完成后 callback || promise.then()里面調(diào)用下一次next的時候又繼續(xù)恢復(fù)控制權(quán)這個功能來實現(xiàn)的
async await 正式利用了這一點
- yield 表達(dá)式交出控制權(quán)
兩種方法可以做到這一點套耕。
(1)回調(diào)函數(shù)谁帕。將異步操作包裝成 Thunk 函數(shù)峡继,在回調(diào)函數(shù)里面交回執(zhí)行權(quán)。
(2)Promise 對象匈挖。將異步操作包裝成 Promise 對象碾牌,用then方法交回執(zhí)行權(quán)康愤。
from http://es6.ruanyifeng.com/#docs/generator-async
- 使generator遍歷器函數(shù)自執(zhí)行
es7 async await 異步寫法
function sleep(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms);
});
}
async function test() {
for (let i = 0; i < 10; i++) {
await sleep(100);
}
}
轉(zhuǎn)化成es6是下邊這樣
async await 實際上是由 generator + yield 控制流程 + promise 實現(xiàn)回調(diào)
// _asyncToGenerator 顧名思義轉(zhuǎn)換async to Generator
- 轉(zhuǎn)換 await 為 yield
- 轉(zhuǎn)換 async 為 Generator
- 調(diào)用test之前 _asyncToGenerator函數(shù) 已經(jīng)執(zhí)行,并傳遞了一個fn (Generator)給匿名的執(zhí)行函數(shù)
- 實際上 調(diào)用test之前, test方法已經(jīng)變成了一個 實現(xiàn) Generator 遍歷器生成函數(shù)自執(zhí)行的 方法
- 調(diào)用test 遞歸執(zhí)行step
- 調(diào)用next的來執(zhí)行遍歷器
- 每次執(zhí)行返回一個promise
- promise的then方法里執(zhí)行next方法)
- 使得Generator 遍歷器自動 執(zhí)行舶吗, 從而達(dá)到異步
function _asyncToGenerator(fn) {
return function () {
var gen = fn.apply(this, arguments);
return new Promise(function (resolve, reject) {
function step(key, arg) {
try {
var info = gen[key](arg);
var value = info.value;
} catch (error) {
reject(error);
return;
}
if (info.done) {
resolve(value);
} else {
return Promise.resolve(value).then(function (value) {
return step("next", value);
},
function (err) {
return step("throw", err);
});
}
}
return step("next");
});
};
}
function sleep(ms) {
return new Promise((resolve, reject) => {
setTimeout(resolve, ms);
});
}
let test = function () {
var ref = _asyncToGenerator(function* () {
for (let i = 0; i < 10; i++) {
yield sleep(100);
}
});
return function test() {
return ref.apply(this, arguments);
};
}();