generator是一個狀態(tài)保存機,每次遇到y(tǒng)ield會停止,需要調用next函數(shù)來執(zhí)行
function * test1(){
yield 3+2
console.log('a')
yield 7
return 5
}
let t = test1()
t.next()//{value:5,done:false}
t.next()//a,{value:7,done:false}
t.next()//{value:5,done:true}
而yield返回的值是無法直接捕捉到的
function * test1(){
let a = 8+(yield 3+2)
console.log(a)
}
let t = test()
t.next()//{value:5,done:false}
t.next()//NaN,{value:undefined,done:true}
因為yield不會賦值給左邊a,永遠等于undefined,8+undefined就返回NaN了,
而在next函數(shù)中傳值可以等于賦值給上一個yield的結果
function * test1(){
let a = 8+(yield 3+2)
console.log(a)
}
let t = test()
t.next()//{value:5,done:false}
t.next(6)//14,{value:undefined,done:true}
這一次我們第二次給next傳值6,則a = 8 + 6 =14,我們沒寫return,默認return為undefined,所以value為undefined
thunk函數(shù)
thunk函數(shù)起源于函數(shù)的求值方式
function add(a,b)
變成
addThunk(a)(b)
一個readFile的thunk可以這么寫
var Thunk = function (fn) {
return function (...args) {
return function (callback) {
return fn.call(this, ...args, callback)
}
}
}
var readFileThunk = Thunk(fs.readFile)
readFileThunk('./1.txt')(function(err,data){})
這是一種惰性求值方式,但是結合generator卻很好地控制流程,原因是可以把函數(shù)和它的回調抽離出來,如果我們在把回調抽象成一個函數(shù),集中處理這個next指針,那么寫法就和同步代碼無異了
這樣的話,next指針在上一個函數(shù)的回調里執(zhí)行,因此只有上一個異步調用返回以后才會繼續(xù)往下執(zhí)行。
function run(fn) {
var gen = fn();
function next(err, data) {
var result = gen.next(data);
if (result.done) return;
result.value(next);
}
next();
}
var g = function* () {
var f1 = yield readFileThunk('./1.txt');
console.log(f1.toString());
var f2 = yield readFileThunk('./2.txt');
console.log(f2.toString());
var fn = yield readFileThunk('./3.txt');
};
run(g);
上面的next函數(shù),實際就是我們抽離出來控制next指針的
第一次執(zhí)行,調用run函數(shù),傳入g函數(shù),執(zhí)行后返回gen
調用next(),err和data均為undefined,此時result為{value:function readFileThunk('./1.txt'),done:false}
然后再調用result.value(next),就把next函數(shù)繼續(xù)放入回調中
第二次:
readFileThunk('./1.txt')返回結果,此時調用上一步的回調函數(shù)next,傳入err和data,在gen.next傳入data,那么上一步返回的結果就為data,因此f1就為讀取1.txt的結果,它通過在下一次回調中,把結果通過gen.next傳回去實現(xiàn)的。
基本就是這種方式實現(xiàn)異步控制的,關鍵就是控制權的處理上