請教一個世界級的初學者難題,關於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(); 的返回值啦
});

co 就是一個 「generate function」 執行器,我們來看看他是怎麼處理的;

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 |