【js】generator和thunk

一、thunk

可以把thunk函数理解为函数柯里化的一个子集,作用是把多个参数的函数,转化为以一个回调为参数的函数

方便和gennerator的配合

生产环境下可以用thunkify组件

npm i thunkify 

然后,我们就可以得到yield后边的带有回调参数的函数,当然yield会把这个函数,封装到一个对象的value属性里

类似于{value:fn,done:'...'}的形态,然后在run函数中调用判断

var fs = require('fs');

var thunkify = require('thunkify');

//这就是一个thunk封装,加上文件名参数之后就是一个完整的thunk函数,即一个带有回调参数的函数

var readFile = thunkify(fs.readFile);

var gen = function* (){

  var r1 = yield readFile('/etc/fstab');

  ....

};

thunkify的大致实现原理:

//最外层传入需要封装的函数,它只是一个函数名,需要我们再搭配参数才完整

var Thunk = function(fn){

return function (){

    //将接受到的参数转化为参数数组,arguments可以理解为函数的实参

    var args = Array.prototype.slice.call(arguments);,

    //这里返回函数,带有回调参数

    return function (callback){

        args.push(callback);//在实参的最后加入了callback,

        //规范作用域,只不过外部封装的只有一个回调,this相当于调用它的对象,

        //在内部形态和最初的readfile的形态实际上是一致的

        return fn.apply(this, args);

    }

  };

};

使用的时候:

var readFileThunk = Thunk(fs.readFile);

//这就是我们最终要转换的形态,主要是要记住这个形态

readFileThunk(fileA)(callback);

注意:凡是有回调的函数都可以转化为thunk函数 

二、generator

function 后面带 * 的叫做generator。

在generator内部你可以使用 yield 语句:

function* genFunc () {

    console.log('step 1')

    yield 1

    console.log('step 2')

    yield 2

    console.log('step 3')

    return 3

}

当你调用一个generator函数的时候,你会获得一个iterator对象。

var gen = genFunc()

这个对象有一个方法叫做 next()

每当你调用 next() 的时候,generator函数内部就会执行直到遇到下一个 yield 语句,然后暂停在那里,并返回一个对象。这个对象含有被 yield 的值和generator函数的运行状态。

var ret = gen.next() // 输出: 'step 1'

console.log(ret.value) // 1

console.log(ret.done) // false

可以看到,只输出了 'step 1'。这意味着直到你运行下一次 next() 之前,generator内部的状态处于暂停之中,但是却不影响generator外部的代码继续运行。

ret = gen.next() // 输出 'step 2'

console.log(ret.value) // 2

console.log(ret.done) // false

直到generator函数内部不再有 yield 语句存在了,这时你再调用 next(),获得的就会是该函数的常规返回值 (return 的值):

ret = gen.next() // 输出 'step 3'

console.log(ret.value) // 3

console.log(ret.done) // true

同时,iterator对象的 next() 方法是可以传递一个参数的。这个参数将会成为generator函数内对应 yield 语句的返回值:

function* genFunc () {

    var result = yield 1

    console.log(result)

}

var gen = genFunc()

gen.next() // 此时generator内部执行到 yield 1 并暂停,但还未对result赋值!

// 即使异步也可以!

setTimeout(function () {

    gen.next(123) // 给result赋值并继续执行,输出: 123

}, 1000)

虽然本意是用来提供一个可循环对象,但可以看到,generator函数可以借助 yield 在需要的时候才继续执行剩余的语句,并且传递回一个值。

这让你想到了什么?没错,回调函数!

更关键的是,借助generator我们可以用同步的逻辑来表达异步的流程,而不需要嵌套回调。我们只需要对创建iterator对象、调用next()以及外部函数做一些适当的封装和修改,就可以获得无回调的异步流程控制。

三、以下就是一些修改和封装,co库早期版本的实现原理也是如此

//1.把next的过程包装了一下,触发之后自动执行,把控制器传进来,依次执行
 
function run(fn) {
 
  var gen = fn();
 
  function next(err, data) {
 
    var result = gen.next(data);
 
    if (result.done) return;
 
    result.value(next);//递归
  }
 
  next();//触发一次,剩下的就交给自动递归执行了
 
}
 
//2.控制器是generator
 
var gen = function* (){
 
  // 3.yield跟的是thunk函数,返回一个以回调为参数的函数,方便run方法重复调用
 
  var f1 = yield readFile('fileA’);
 
  var f2 = yield readFile('fileB');
  // ...
  var fn = yield readFile('fileN');
};
 
run(gen);
 

POST:

http://ioqq.com/node-js-%E4%B8%AD-function-next-%E7%94%A8%E6%B3%95%EF%BC%9F.html

https://www.jianshu.com/p/21d941132e42

http://www.ruanyifeng.com/blog/2015/05/thunk.html

发布了283 篇原创文章 · 获赞 21 · 访问量 20万+

猜你喜欢

转载自blog.csdn.net/dangbai01_/article/details/103103676
今日推荐