請教一個世界級的初學者難題,關於node非同步,Thunk,co的問題,請教下大神?
最近在學習node,koa,es6時,遇到了像下面這個很頭疼的問題。
下面兩個函數fun1和fun2,fun1是我勉強能理解的;fun2簡直爆炸,完全看不懂的說,所以請教下各位大神?——————————————————————————————12行到15行,我實在實在看不懂。。。。——————————————————————————————————下面粘出代碼:var co = require("co");var fun1 = function() { return co( function*() { return "輸出結果A";
} );};var fun2 = function() { return function(cb) { cb(null, "輸出結果B"); return; }};
co( function *(){ var aaa = yield fun1(); console.log(aaa); var bbb = yield fun2(); console.log(bbb); });
fun2 不能理解,fun1 也沒有真正理解吧
要理解 co 這種寫法首先要理解這個:function* generator() {
var ret = yield funInGenerator();
}
ret 絕不一定是 fun() 的返回值,ret 是什麼要看這個 「generate function」 執行器是怎麼處理的;
var gen = generator();
var ret1 = gen.next();
var val1 = ret1.value;
// 題主在學 es6,上面兩行代碼肯定明白,val1 就是 funInGenerator 的返回值
// 假設 funInGenerator 就是題主問題中的 fun2
// 我們知道調用 gen.next(param); 會在調用 generator 中下一個 yield 後的函數的同時將 param 作為上一個 yield 語句的返回值;
val1(function (err, res) {
gen.next(res);
// 通過這樣的處理 fun2 內函數的 cb 的第二個參數就成為了 yield funInGenerator(); 的返回值啦
});
function co(gen) {
var ctx = this;
var args = slice.call(arguments, 1);
// we wrap everything in a promise to avoid promise chaining,
// which leads to memory leak errors.
// see https://github.com/tj/co/issues/180
return new Promise(function(resolve, reject) {
if (typeof gen === "function") gen = gen.apply(ctx, args);
if (!gen || typeof gen.next !== "function") return resolve(gen);
onFulfilled();
/**
* @param {Mixed} res
* @return {Promise}
* @api private
*/
function onFulfilled(res) {
var ret;
try {
ret = gen.next(res);
} catch (e) {
return reject(e);
}
next(ret);
return null;
}
/**
* @param {Error} err
* @return {Promise}
* @api private
*/
function onRejected(err) {
var ret;
try {
ret = gen.throw(err);
} catch (e) {
return reject(e);
}
next(ret);
}
/**
* Get the next value in the generator,
* return a promise.
*
* @param {Object} ret
* @return {Promise}
* @api private
*/
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) + """));
}
});
}
function toPromise(obj) {
if (!obj) return obj;
if (isPromise(obj)) return obj;
if (isGeneratorFunction(obj) || isGenerator(obj)) return co.call(this, obj);
if ("function" == typeof obj) return thunkToPromise.call(this, obj);
if (Array.isArray(obj)) return arrayToPromise.call(this, obj);
if (isObject(obj)) return objectToPromise.call(this, obj);
return obj;
}
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);
});
});
}
可以看到 co 在處理 gen.next 返回值的時候,如果發現其返回值是 function,說明 yield 的是 thunk,會將 thunk 轉換成 promise,然後在 promise 里調用 cb,並且將 cb 第一個參數後面的參數封裝成 res 傳給 resolve 函數,然後繼續調用 promise 的 then 獲取到 res,然後調用 onFulfilled,onFulfilled 里又調用了 gen.next(res); 最終實現了 把 res 作為 yield fun(); 返回值的功能;
大概的邏輯就是這樣,如果要真正搞懂這些的話還需要仔細學習 co 的實現方式和 generator;
參考:function - JavaScript*co/index.js at master · tj/co · GitHub
推薦我寫的一步步寫一個co · GitBook,嘗試了一種新的源碼分析方式,就是自己從零開始擼一個類似co v2的庫,co這個東西,直接用可以,但是建議閱讀下源碼,理解下設計哲學,很有意思的東西
首次回答問題,誠惶誠恐co會判斷當前yield的對象,如果yield的是一個thunk函數將會使用以下代碼將其轉換為promise:
/**
* Convert a thunk to a promise.
*
* @param {Function}
* @return {Promise}
* @api private
*/
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);
});
});
}
對應題主的示例,yield fun2() = yield (cb) =&> { ... },其實就是yield一個thunk函數
function (cb) {
cb(null, "輸出結果B");
return;
}
即thunkToPromise中的fn參數,所以實際上cb就是thunkToPromise中的function (err, res) { ... }
謝邀。請看Thunk 函數的含義和用法看完你就懂了。
故意讓你看不懂的代碼, 你為什麼要看懂它?就像你永遠得不到的女神,你為什麼要靠近她?
這個其實就是co執行器里的東西了,通過co執行時會自動調用callback搞定 koa 之 co源碼參考下這個
返回一個函數,該函數有一個參數,該參數是一個函數,該參數函數有倆個參數。即該函數返回一個具有倆個參數的函數作為參數的函數。
推薦閱讀:
TAG:JavaScript | Nodejs | ECMAScript2015 | koa | co |