• 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;
      });
    }

  • 相关阅读:
    leetcode 104. Maximum Depth of Binary Tree 二叉树的最大深度(简单)
    leetcode 105. Construct Binary Tree from Preorder and Inorder Traversal 从前序与中序遍历序列构造二叉树(中等)
    leetcode 83. Remove Duplicates from Sorted List 删除排序链表中的重复元素(简单)
    leetcode 637. Average of Levels in Binary Tree 二叉树的层平均值(简单)
    Fiddler的安装与使用
    Redis
    开发那些事儿:如何解决js打包文件体积过大导致的网页加载慢问题?
    AI人工智能识别技术如何助力构建风险监测预警系统?
    H.265流媒体播放器EasyPlayer切换播放协议时,快照无法消失如何处理?
    AI人脸检测/行为识别智能分析网关8大智慧应用场景分析
  • 原文地址:https://www.cnblogs.com/mengff/p/12787601.html
Copyright © 2020-2023  润新知