generator自執行問題和co原始碼解析
generator自動執行問題
這裡我覺得對於js引擎,其實仍然是單執行緒的。能達到這麼一種效果,應該是generator函式內部的資料結構的原因。迭代器內部自己能快取其各種狀態。就是一種迭代器。(經我試驗,呼叫方式同步,在其中完成非同步操作時候,如果gen中掉了非同步,非同步的執行還是會在同步之後)
這是一種迭代器,但是在主流node裡,主要用它將非同步變為同步作用。 —– 這裡我認為是利用這種機制實現,本身這種資料結構並沒有這樣的。
柯里化的概念很簡單:只傳遞給函式一部分引數來呼叫它,讓它返回一個函式去處理剩下的引數。
在電腦科學中,柯里化(英語:Currying),又譯為卡瑞化或加里化,是把接受多個引數的函式變換成接受一個單一引數(最初函式的第一個引數)的函式,並且返回接受餘下的引數而且返回結果的新函式的技術。
柯里化的用處是感覺簡化了函式程式設計。讓函式程式設計過程更簡單。 —- 函數語言程式設計還可以再說
當我們走到generator時候,其實就是用yield執行函式並且出讓程式碼的執行權、當我們使用next函式的時候,就可以yield再還回來程式碼的執行權。也就是說讓next自動執行。而自動執行的思路無非是遞迴和迭代。在遞迴和迭代中呼叫next函式。
有大佬寫了個庫co,在co庫中使用兩種方式(一種是用thunk傳入回撥函式,一種是用promise的then來傳入回撥函式來交還執行權)
方式是非常精妙的!!!!
co(function),代表這個函式要用co這個方式進行非同步變同步。就是不停的走迭代器(通過next),如果迭代器返回的當前狀態是沒有結束仍在迴圈中,並且迭代器返回的是一個函式(比如說我們非同步時候,其實就是放一個函式給yield狀態的)。那麼這個時候我們就取迭代器的value值執行它,並將回撥函式傳入–co最經典的地方就是通過傳遞迴調函式,讓結束非同步操作後代碼又回到這個迭代器裡!!!!
回撥函式裡,因為閉包,這個非同步函式可以使用當前變數。回撥函式裡再次呼叫了這個next函式,起到了往下走的同步思路。因為是在回撥函式裡寫的,所有非同步妥妥的。
— 這裡就用到了thunk想法,將函式傳入,代需要執行的時候再執行引數。
程式碼如下:(這個思想的一個簡單例子,不是大神的原始碼,但是基本就是這個實現思路)
co(function *( input ) {
var now = Date.now();
yield sleep200;
console.log(Date.now() - now);
});
function co(fn){
var gen = fn();
next();
function next(res){
var ret;
ret = gen.next(res);
// 全部結束
if(ret.done){
return;
}
// 執行回撥
if (typeof ret.value == 'function' ) {
ret.value(function(){
next.apply(this, arguments);
});
return;
}
throw 'yield target no supported!';
}
}
function sleep200(cb){
setTimeout(cb, 200)
}
那麼按照上述思路,其實我們能做到在非同步完成後在回撥裡執行value.next,讓genertor迭代函式重新獲得執行權繼續執行就可以了。要打成這個目的有兩種方式:
用thunk偏函式的方式,如上述程式碼,將回調函式作為引數傳入給非同步執行的函式;
用promise思想,resolve裡會執行next;(4.0後版本就是這樣的)
現在的版本里核心函式同樣是回撥,但是由於使用了promise還有個核心是promise封裝
/**
* 這個函式裡執行每次做promise和往後移 ---- 第二次後就是回撥函式
**/
function onFulfilled(res) {
var ret;
try {
ret = gen.next(res);
} catch (e) {
return reject(e);
}
next(ret);
}
/**
* 這個函式裡起到封裝promise,執行這個非同步函式,並且還是在回撥中再交回執行權,
* 這裡就是用promise.then(resolve,rejecct) 這樣的
**/
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) + '"'));
}
4.0版本里面主要是圍繞這個思路的,剩下的無非就是為了使用者體驗,把各個value給的東西封裝成promise函式,這樣就能滿足上面的回到條件和處理異常(處理異常也是很重要的一點,會丟擲異常),具體而言有封裝這幾個型別到promise:
- 不是Object的基礎資料型別無非同步需求,可以直接返回做同步操作
- generatorFunction和Generator,給co裡帶上系統環境變數為當前的value
- function ,用thunk那種回撥函式的方式,將其封裝成promise
- array,array封裝成promise
- object,封裝成promise
最正常的普通函式的封裝方式如下:
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);
});
});
}