項目中使用了koa,最近在學習koa的源碼,先把這些東西都寫下來,省得之後忘記了。node
koa源碼學習前先注意下面這三個概念es6
generator function (生成器函數)chrome
generator (生成器)express
yield瀏覽器
下面是一坨很簡單的代碼koa
function *gen() {ide
yield 'sd'; } var g = gen();
上面的代碼中 gen是一個generator function, g是一個generator, yield 只是一個語法糖,下面會具體介紹函數
iterator和generator特性借鑑於Python, Ruby, smalltalk, 原本用於方便訪問容器內各個元素. 該特性須要node v0.11.9並開啓–harmony特性才能使用, 在chrome(29+)瀏覽器中須要在chrome://flags/ 開啓Enable Experimental JavaScript選項, 而後重啓.
一個迭代器(對象)會有一個名爲 next 的方法, 調用該方法後會返回一個擁有兩個屬性的對象, 一個是 value 屬性, 值能夠是任意值, 以及一個 done 屬性, 布爾值, 表示該迭代器是否已經被迭代完畢, 相似{done: true/false, value: returnValue}結構數據 小貼士: String, Array, TypedArray, Map and Set 都是內建的迭代器, 一些表達式但願後面是iterable的,好比for of , yield*, 和析構複製 [..."abcd"] //看下這個語句返回什麼呢?
此外,generator 還有一個throw 方法,能夠進行異常處理
generator 就是一個迭代器, 含有next方法
每當調用 next() 的時候,generator function內部就會執行直到遇到下一個 yield 語句,而後暫停在那裏,並返回一個對象。ui
普通函數添加*號後則成爲了成爲了生成器函數了。
// 定義生成器函數
function *enumerable(msg){ console.log(msg) var msg1 = yield msg + ' after ' console.log(msg1) var msg2 = yield msg1 + ' after' try{ var msg3 = yield msg2 + 'after' console.log('ok') }catch(e){ console.log(e) } console.log(msg2 + ' over') } // 初始化迭代器 var enumerator = enumerable('hello') var ret = enumerator.next() // 控制檯顯示 hello,ret的值{value:'hello after',done:false} ret = enumerator.next('world') // 控制檯顯示 world,ret的值{value:'world after',done:false} ret = enumerator.next('game') // 控制檯顯示game,ret的值{value:'game after',done:false} // 拋出異常信息 ret = enumerator.throw(new Error('test')) // //控制檯顯示new Error('test')信息,而後顯示game over。ret的值爲{done:true}
生成器函數的行爲與普通函數並不相同,表現爲以下3點:
經過new運算符或函數調用的形式調用生成器函數,均會返回一個生成器實例;
經過new運算符或函數調用的形式調用生成器函數,均不會立刻執行函數體的代碼;
必須調用生成器實例的next方法纔會執行生成器函數體的代碼。
用於立刻退出代碼塊並保留現場,當執行迭代器的next函數時,則能從退出點恢復現場並繼續執行下去。
一旦在 yield expression 處暫停, 除非外部調用生成器的 next() 方法,不然生成器的代碼將不能繼續執行. 這使得能夠對生成器的執行以及漸進式的返回值進行直接控制.
下面有2點須要注意:
yield後面的表達式將做爲迭代器next函數的返回值;
迭代器next函數的入參將做爲yield的返回值(有點像運算符)。
針對上面的例子,
var ret = enumerator.next()// {value:'hello after',done:false}
enumerator.next('msg1 result');//這時候msg1的值是 msg1 result;
yield* 一個可迭代對象,就至關於把這個可迭代對象的全部迭代值分次 yield 出去。
yield* 表達式自己的值就是當前可迭代對象迭代完畢時的那個返回值(也就是迭代器的迭代值的 done 屬性爲 true 時 value 屬性的值)。
function* g1() {
yield 2; yield 3; yield 4;}
function* g2() {
yield 1; yield* g1(); yield 5;}
var iterator = g2();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: 4, done: false }
console.log(iterator.next()); // { value: 5, done: false }
console.log(iterator.next()); // { value: undefined, done: true}
//返回值例子
function* g4() {
yield* [1, 2, 3]; return "foo";}
var result;
function* g5() {result = yield* g4();}
var iterator = g5();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: undefined, done: true},
此時 g4() 返回了 { value: "foo", done: true }
console.log(result); // "foo"
具體能夠參考yield *
能夠經過throw 拋出異常,在外層try catch, 具體能夠參考這裏
function *foo() {
try { yield 2; } catch (err) { console.log( "foo caught: " + err ); } yield; // pause // now, throw another error throw "Oops!";}
function *bar() {
yield 1; try { yield *foo(); } catch (err) { console.log( "bar caught: " + err ); }}
var it = bar();
it.next(); // { value:1, done:false }
it.next(); // { value:2, done:false }
it.throw( "Uh oh!" ); // will be caught insidefoo()
// foo caught: Uh oh!
it.next(); // { value:undefined, done:true } --> No error here!
// bar caught: Oops!
generator 須要不停地調用next方法,可是在項目中咱們也沒有手動的調用next方法,這是爲何呢?........
很牛的co模塊就要登場了,請翻看下一篇
https://developer.mozilla.org/zh/docs/Web/JavaScript/Guide/Iterators_and_Generators
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield*