zoukankan      html  css  js  c++  java
  • co源码分析(thunk版本3.1.0)

    co的thunk版本,就是将所有 函数,generator,generator function,object,array,promise,都转换为thunk函数,在thunk函数的回调中,切换外部包装的generator的状态,即调用next方法,来依次执行所有的异步任务。其中的,object和array,通过循环的方式,来并行执行thunk函数。

    具体的源码注释如下:

    //方便截取函数参数
    var slice = Array.prototype.slice;
    
    //导出co函数
    module.exports = co;
    
    //Wrap the given generator `fn` and return a thunk.
    //包裹generator,并且返回一个thunk函数,执行这个thunk函数,就可以开始整个异步流程
    //var thunk = co(function* (){...}); 
    //开始执行 thunk();
    function co(fn) {
      var isGenFun = isGeneratorFunction(fn);
    
      return function (done) {
        var ctx = this;
    
        // in toThunk() below we invoke co()
        // with a generator, so optimize for
        // this case
        var gen = fn;
    
        // we only need to parse the arguments
        // if gen is a generator function.
        if (isGenFun) {
          var args = slice.call(arguments), len = args.length;
          //判断最后一个参数是否函数
          var hasCallback = len && 'function' == typeof args[len - 1];
          //末参是回调,done=回调,不是记录错误
          done = hasCallback ? args.pop() : error;
          //是一个generator function,就是执行它,得到一个generator
          gen = fn.apply(this, args);
        } 
        else {
          //不是generator,就为入参,或记录错误
          done = done || error;
        }
        
        //启动执行,next会递归调用,直至所有异步函数执行完毕
        next();
    
        // #92
        // wrap the callback in a setImmediate
        // so that any of its errors aren't caught by `co`
        function exit(err, res) {
          setImmediate(function(){
            done.call(ctx, err, res);
          });
        }
    
        function next(err, res) {
          var ret;
    
          // res相当于err剩余的所有参数
          if (arguments.length > 2) res = slice.call(arguments, 1);
    
          //有错误,generator抛出错误,退出
          if (err) {
            try {
              ret = gen.throw(err);
            } catch (e) {
              return exit(e);
            }
          }
    
          //无错误,gen执行next,执行一次异步函数,
          if (!err) {
            try {
              ret = gen.next(res);
            } catch (e) {
              return exit(e);
            }
          }
    
          // done为true,执行完所有异步函数,退出
          if (ret.done) return exit(null, ret.value);
    
          // 把ret.value得到的函数转换为thunk函数
          ret.value = toThunk(ret.value, ctx);
    
          // ret.value转换为thunk函数成功
          if ('function' == typeof ret.value) {
            var called = false;
            try {
              ret.value.call(ctx, function(){
                //防止重复调用
                if (called) return;
                called = true;
                //递归调用next函数,继续执行下面异步函数,自执行核心部分
                //此function是ret.value这个thunk函数的回调函数
                //arguments是回调函数参数,传递给next,包含了err和res,作为gen的throw和next的参数
                next.apply(ctx, arguments);
              });
            } catch (e) {
              setImmediate(function(){
                if (called) return;
                called = true;
                next(e);
              });
            }
            return;
          }
    
          // ret.value转换为thunk函数失败,提示只能是以下类型
          next(new TypeError('You may only yield a function, promise, generator, array, or object, '
            + 'but the following was passed: "' + String(ret.value) + '"'));
        }
      }
    }
    
    
    //Convert `obj` into a normalized thunk.
    //将任何类型转换为thunk
    function toThunk(obj, ctx) {
      //是generator function,obj.call(ctx)返回generator,再用co调用
      if (isGeneratorFunction(obj)) {
        return co(obj.call(ctx));
      }
      //是generator,用co直接调用
      if (isGenerator(obj)) {
        return co(obj);
      }
      //是promise,转换为thunk函数
      if (isPromise(obj)) {
        return promiseToThunk(obj);
      }
      //是函数,直接返回本身
      if ('function' == typeof obj) {
        return obj;
      }
      //是对象或者数组,转换为thunk函数
      if (isObject(obj) || Array.isArray(obj)) {
        return objectToThunk.call(ctx, obj);
      }
      
      //都不是,直接返回对象,未成功转换为thunk function,co的next函数会报错,提示格式不符合
      return obj;
    }
    
    // Convert an object of yieldables to a thunk.
    // 将thunk数组,或thunk对象,转换为一个thunk函数
    // 即将 arr = [thunk1,thunk2] 转换为 一个thunk
    // 或将 obj = { key1: thunk1, key2: thunk2 } 转换为 一个thunk
    function objectToThunk(obj){
      var ctx = this;
      var isArray = Array.isArray(obj);
    
      return function(done){
        //获取keys,对象是key,数组是索引
        var keys = Object.keys(obj);
        var pending = keys.length;
        //初始化results的为一个同长度数组,或同类型对象
        var results = isArray
          ? new Array(pending) // predefine the array length
          : new obj.constructor();
        var finished;
    
        //对象或数组长度为0,结束
        if (!pending) {
          setImmediate(function(){
            done(null, results)
          });
          return;
        }
    
        // prepopulate object keys to preserve key ordering
        // 对象类型,results按照obj的key,全部初始化为undefined
        if (!isArray) {
          for (var i = 0; i < pending; i++) {
            results[keys[i]] = undefined;
          }
        }
        
        //所有对象或数组key对应的函数传入run函数执行
        for (var i = 0; i < keys.length; i++) {
          run(obj[keys[i]], keys[i]);
        }
    
        function run(fn, key) {
          if (finished) return;
          try {
            //函数转化为thunk函数
            fn = toThunk(fn, ctx);
            
            //fn不是function,则转化为thunk函数失败
            if ('function' != typeof fn) {
              //记录函数本身
              results[key] = fn;
              //pending数量减小,当pending为0时候,执行done,结束
              return --pending || done(null, results);
            }
            
            //fn是thunk函数,执行fn
            fn.call(ctx, function(err, res){
              if (finished) return;
    
              //报错,结束,传递错误
              if (err) {
                finished = true;
                return done(err);
              }
              
              //在results中,用对应的key记录fn执行结果,合法函数最终被记录
              results[key] = res;
              //减小待处理数量,为0,就结束
              --pending || done(null, results);
            });
          } catch (err) {
            //有任何报错,就结束
            finished = true;
            done(err);
          }
        }
      }
    }
    
    //promsie转thunk
    function promiseToThunk(promise) {
      //返回只有一个回调参数fn的函数
      return function(fn){
        //回调fn遵循error first原则,在then的第一个回调中,error是null,data是res,传入fn(null,res)
        //在then的第二个回调中,fn直接用作error callback,只接收一个err参数
        //最终fn是形如 fn(err,data) 这种形式
        promise.then(function(res) {
          fn(null, res);
        }, fn);
      }
    }
    
    //判断是Promise
    function isPromise(obj) {
      return obj && 'function' == typeof obj.then;
    }
    
    //generator function的返回值,就是一个interator
    function isGenerator(obj) {
      return obj && 'function' == typeof obj.next && 'function' == typeof obj.throw;
    }
    
    // 形如 function* (){...} 都是 generator function
    function isGeneratorFunction(obj) {
      return obj && obj.constructor && 'GeneratorFunction' == obj.constructor.name;
    }
    
    //判断是对象
    function isObject(val) {
      return val && Object == val.constructor;
    }
    
    /**
     * Throw `err` in a new stack.
     *
     * This is used when co() is invoked
     * without supplying a callback, which
     * should only be for demonstrational
     * purposes.
     *
     * @param {Error} err
     * @api private
     */
    
    function error(err) {
      if (!err) return;
      setImmediate(function(){
        throw err;
      });
    }

  • 相关阅读:
    操作系统概述
    18 文本处理
    17 正则表达式(重点)
    16 归档和备份
    15 查找文件
    博客园图片折叠
    EF6学习笔记十五:调试EF框架源码
    EF6学习笔记十四:上下文管理
    Sqlserver和LocalDB创建数据库时默认字符集不一样
    EF6学习笔记十三:基础知识完结,零碎问题补缺
  • 原文地址:https://www.cnblogs.com/mengff/p/12787601.html
Copyright © 2011-2022 走看看