Generator是ES6一個頗有意思的特性,也是不容易理解的特性。不一樣於let/const
提供了塊級做用域這樣明顯的目的,這玩意兒被搞出來究竟是幹嗎的?javascript
首先咱們須要明確一個問題,在JavaScript中,任何一個函數只要開始執行,便沒法中止下來直到執行完成(別跟我提debug,你見過哪一個用戶在使用產品的還得開個debug調試你的代碼)。java
but,Generator提供這種能力。 看下面代碼:網絡
function *g(){ console.log('start'); yield 1; console.log('middle'); yield 2; console.log('end'); } var g1 = g(); console.log(g1.next()); // start // {value: 1, done: false} console.log(g1.next()); // middle // {value: 2, done: false} console.log(g1.next()); // end // {value: undefined, done: true}
根據輸出結果,咱們看到,在函數g
中,碰到yield
關鍵詞,運行的程序會停下來。只有調用 next()
方法,纔會繼續執行函數g
中的代碼。因此函數g
自己有暫停狀態。app
至此,咱們須要知道:框架
既然Generator能夠函數停下來,有些腦洞清奇的人,想到了可不能夠用Generator處理異步程序。異步
先看一個傳統例子:async
function asyn(fn) { return new Promise((resolve,reject)=>{ setTimeout(()=>{ fn(); resolve(true); }, 1000); }); } function main() { console.log('start'); asyn(function(d) { console.log('async one'); asyn(function(d) { console.log('async two'); console.log('end'); }); }); } main();
再來看看使用了Generator的異步程序:函數
function asyn(fn) { return new Promise((resolve,reject)=>{ setTimeout(()=>{ fn(); resolve(true); }, 1000); }); } co(function*() { console.log('start'); yield asyn(function(d) { console.log('async one'); }); yield asyn(function(d) { console.log('async two'); }); console.log('end'); }); function co(fn) { return new Promise((resolve,reject)=>{ let g = fn(); onFullfilled(); function onFullfilled() { let ret = null; ret = g.next(); next(ret); } function next(ret) { if(ret.done) return resolve(ret.value); ret.value.then(onFullfilled); } } ); }
函數在異步程序沒有采用嵌套回調,是直接用同步的方式寫了出來。道理大概就是,有兩個異步程序,用小明和小紅指代它們。小紅須要等小明執行完了才能夠被執行,那麼咱們在執行到小明時,暫停程序的運行(經過yield
),等到小明有了返回結果時,再執行後面跟着的小紅(next()
)。debug
在上面的程序中,咱們添加了一個co
函數,這個函數的做用是讓Generator自動執行下去。直白來講,就是當第一個異步函數返回後,自動調用next()
方法運行後面的代碼。調試
Koa是基於Node.js的Web應用框架。在Koa中,處理的異步程序主要是網絡請求(HTTP)、文件讀取和數據查詢。這裏面的異步場景較多,若是再加上程序分層,採用傳統的callback方式,那回調多了去了。
app.on('get', function(){ auth(function(){ router(function(){ find(function(){ save(function(){ render(function(){ //...... }) }) }) }) }) })
這樣寫法對於程序維護及其不利,毫無便捷性可言。在有了generator後,咱們即可以像上面那種方式來寫程序。Koa最初的版本就是經過這種方式,讓中間處理程序都轉成一個個"yield"(中間件)。經過中間件的形式去處理客戶端請求,讓開發App應用更加靈活,不受框架自身限制。
在最新的Koa2中,已經拋棄了Genetator,轉而使用async/await
。
可是不管採用哪一種方式,其本質都是利用了Promise。