generator是一个状态保存机,每次遇到yield会停止,需要调用next函数来执行
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函数中传值可以等于赋值给上一个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函数
thunk函数起源于函数的求值方式
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却很好地控制流程,原因是可以把函数和它的回调抽离出来,如果我们在把回调抽象成一个函数,集中处理这个next指针,那么写法就和同步代码无异了
这样的话,next指针在上一个函数的回调里执行,因此只有上一个异步调用返回以后才会继续往下执行。
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函数,实际就是我们抽离出来控制next指针的
第一次执行,调用run函数,传入g函数,执行后返回gen
调用next(),err和data均为undefined,此时result为{value:function readFileThunk('./1.txt'),done:false}
然后再调用result.value(next),就把next函数继续放入回调中
第二次:
readFileThunk('./1.txt')返回结果,此时调用上一步的回调函数next,传入err和data,在gen.next传入data,那么上一步返回的结果就为data,因此f1就为读取1.txt的结果,它通过在下一次回调中,把结果通过gen.next传回去实现的。
基本就是这种方式实现异步控制的,关键就是控制权的处理上