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);
    });
  });
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值