# co
理解了co的核心代碼就理解了koa的流程控制
```js
var ctx = this;
var args = slice.call(arguments, 1);
```
一開始保存上下文,把arguments的length屬性去掉,剩余的參數轉數組就是gen的參數
再來看return的promise內的代碼
```js
if (typeof gen === 'function') gen = gen.apply(ctx, args);
if (!gen || typeof gen.next !== 'function') return resolve(gen);
```
先判斷gen是不是generator function,如果是就轉為generator,相當于```gen = new gen;```
轉為generator之后就可以調用gen.next()了;
```
onFulfilled();
```
這是進入循環調用鏈的入口
```js
function onFulfilled(res) {
var ret;
try {
ret = gen.next(res);
} catch (e) {
return reject(e);
}
next(ret);
}
function onRejected(err) {
var ret;
try {
ret = gen.throw(err);
} catch (e) {
return reject(e);
}
next(ret);
}
function next(ret) {
if (ret.done) return resolve(ret.value);
var value = toPromise.call(ctx, ret.value);
if (value && isPromise(value)) return value.then(onFulfilled, onRejected);
return onRejected(new TypeError('You may only yield a function, promise, generator, array, or object, '
+ 'but the following object was passed: "' + String(ret.value) + '"'));
}
```
ret是gen.next后的{value:''done:''}對象,value是yield后的表達式,done是執行狀態.
判斷ret.done是否為true來確定是否需要再執行下去.為true時,說明已經是generator的最后一步,promise轉為resolve.不為true時,將yield后的表達式轉化為promise.
先判斷是否轉化為了promise,轉化成功,就通過```value.then(onFulfilled, onRejected)```執行onFulfilled或onRejected,再次調用next(),實現循環調用.
當value不能轉為promise時,拋出錯誤,promise轉為reject,停止繼續運行.
下面寫一個例子簡單分析一下:
```js
var co = require('co');
var fs = require('fs');
function thunkRead(name) {
return function (cb) {
fs.readFile(name, function (err, file) {
cb(err, file);
});
}
}
co(function *() {
var file = yield thunkRead("package.json");
console.log(file);
return file;
}).then(function (file) {
console.log(file);
});
```
通過上面這段代碼來看一下co的整個流程
先模擬一個名為thunkRead的thunk函數,再看co里面的代碼,co里面是一個generator function,```gen = gen.apply(ctx, args);``` 通過這一句轉化為了generator。
再進入onFulfilled()函數,第一個gen.next()之后ret是 ```{ value: [Function], done: false }``` 將ret傳入next()中,done為false,所以toPromise,value是function,所以thunkToPromise.
```js
function thunkToPromise(fn) {
var ctx = this;
return new Promise(function (resolve, reject) {
fn.call(ctx, function (err, res) {
if (err) return reject(err);
if (arguments.length > 2) res = slice.call(arguments, 1);
resolve(res);
});
});
}
```
因為fn是thunk函數,參數只有一個回調函數, ```fn.call(ctx, function (err, res) {});``` 直接調用resolve,在上面的例子中是resolve(file);
然后回到next()中,此時已經是一個promise對象,調用value的then方法,onFulfilled的參數就是file,再運行gen.next(file),將上一步yield的結果file傳入generator,因為在例子中最后return了file, ret是 ```{ value:<Buffer ...>, done: true }``` 最后不返回值的話應該是 ```{ value: undefined, done: true }
``` , 再進入到next()中, 此時done已經為true,說明已經是generator的最后一步,resolve(value);
co中的代碼已經執行結束,因為co也是一個promise, 最后resolve(value),所有可以在then方法中得到這個value.
補充下toPromise支持轉化thunks,array,objects,generators,generator functions.
所以可以yieldable的是以下6種:
- promises
- thunks (functions)
- array (parallel execution)
- objects (parallel execution)
- generators (delegation)
- generator functions (delegation)
- Introduction
- Nodejs 4.x新特性
- classes
- typed arrays
- generators
- collections
- Set
- Map
- arrow functions
- block scoping
- template strings
- promises
- symbols
- Koa基礎
- 上下文
- koa-generator
- 安裝
- 創建項目
- 更改視圖模板引擎
- Routes
- HTTP
- Get
- 如何獲取query參數
- 如何獲取params
- Post
- 從post獲取參數
- 標準表單(Post with x-www-form-urlencoded)
- 文件上傳(Post with form-data)
- Post with raw
- 數據庫
- MySQL
- Mongo
- 流程控制
- generator/co
- es6的generator是什么?
- co = generator + promise
- async/await
- promise with bluebird
- 測試
- Mocha
- Supertest
- 部署
- 最佳實踐
- FAQ
- 如何發布本書到git pages
- 如何知道require模塊的用法
- koa中的異常處理