generator异步调用原理

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传回去实现的。

基本就是这种方式实现异步控制的,关键就是控制权的处理上

你可能感兴趣的:(generator异步调用原理)