资讯专栏INFORMATION COLUMN

co源码解读

xavier / 2918人阅读

摘要:提取函数方便使用暴露把函数包起来,返回执行不带参数的函数,返回如果是函数,直接执行,指定作域判断等价于,判断函数是否执行完根据返回值是否,执行完就通过返回,外部将会执行函数带参数用于修改上一次结果,引擎会忽略第一次执行时参数用于捕获错误执行

/**
 * 提取slice()函数方便使用
 */

var slice = Array.prototype.slice;

/**
 * 暴露 `co`.
 */

module.exports = co["default"] = co.co = co;

/**
 * 把generator函数 `fn` 包起来,返回promise
 * This is a separate function so that
 * every `co()` call doesn"t create a new,
 * unnecessary closure.
 *
 * @param {GeneratorFunction} fn
 * @return {Function}
 * @api public
 */

co.wrap = function (fn) {
  createPromise.__generatorFunction__ = fn;
  return createPromise;
  function createPromise() {
    return co.call(this, fn.apply(this, arguments));
  }
};

/**
 * 执行不带参数的generator函数,返回promise
 *
 * @param {Function} fn
 * @return {Promise}
 * @api public
 */

function co(gen) {
  var ctx = this;
  var args = slice.call(arguments, 1);

  // we wrap everything in a promise to avoid promise chaining,
  // which leads to memory leak errors.
  // see https://github.com/tj/co/issues/180
  return new Promise(function(resolve, reject) {
    //如果是函数,直接执行,apply指定作域
    if (typeof gen === "function") gen = gen.apply(ctx, args);
    // 判断等价于 !(gen && typeof gen.next === "function"),判断generator函数是否执行完(根据返回值是否Iterator Object),执行完就通过resolve返回,外部将会执行`.then`函数
    if (!gen || typeof gen.next !== "function") return resolve(gen);

    onFulfilled();

    /**
     * .next带参数用于修改上一次yield结果,V8引擎会忽略第一次执行时参数
     * try/catch 用于捕获错误
     * 执行完的结果交给自定义的next函数,res结构应该是 {value, done}
     */

    function onFulfilled(res) {
      var ret;
      try {
        ret = gen.next(res);
      } catch (e) {
        return reject(e);
      }
      next(ret);
      return null;
    }

    /**
     * 自己捕获err
     * 注意,这里是调用generator遍历器上的throw方法
     * 一个generator执行出错后就不再执行,返回{value:undefined, done: true},故这里调用next函数会在第一个if结束
     */

    function onRejected(err) {
      var ret;
      try {
        ret = gen.throw(err);
      } catch (e) {
        return reject(e);
      }
      next(ret);
    }

    /**
     * 首先检查是否结束
     * 没有结束就把结果value转化为Promise类型
     * 转化成功就执行Promise,这里是被onFulfilled调用,现在又反过去调用onFulfilled,形成递归。
     * 失败就调用onRejected函数
     */

    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) + """));
    }
  });
}

/**
 * Convert a `yield`ed value into a promise.
 *
 * @param {Mixed} obj
 * @return {Promise}
 * @api private
 */

function toPromise(obj) {
  if (!obj) return obj;
  if (isPromise(obj)) return obj;
  if (isGeneratorFunction(obj) || isGenerator(obj)) return co.call(this, obj);
  if ("function" == typeof obj) return thunkToPromise.call(this, obj);
  if (Array.isArray(obj)) return arrayToPromise.call(this, obj);
  if (isObject(obj)) return objectToPromise.call(this, obj);
  return obj;
}

/**
 * Convert a thunk to a promise.
 *
 * @param {Function}
 * @return {Promise}
 * @api private
 */

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

/**
 * Convert an array of "yieldables" to a promise.
 * Uses `Promise.all()` internally.
 *
 * @param {Array} obj
 * @return {Promise}
 * @api private
 */

function arrayToPromise(obj) {
  return Promise.all(obj.map(toPromise, this));
}

/**
 * Convert an object of "yieldables" to a promise.
 * Uses `Promise.all()` internally.
 *
 * @param {Object} obj
 * @return {Promise}
 * @api private
 */

function objectToPromise(obj){
  var results = new obj.constructor();
  var keys = Object.keys(obj);
  var promises = [];
  for (var i = 0; i < keys.length; i++) {
    var key = keys[i];
    var promise = toPromise.call(this, obj[key]);
    if (promise && isPromise(promise)) defer(promise, key);
    else results[key] = obj[key];
  }
  return Promise.all(promises).then(function () {
    return results;
  });

  function defer(promise, key) {
    // predefine the key in the result
    results[key] = undefined;
    promises.push(promise.then(function (res) {
      results[key] = res;
    }));
  }
}

/**
 * Check if `obj` is a promise.
 *
 * @param {Object} obj
 * @return {Boolean}
 * @api private
 */

function isPromise(obj) {
  return "function" == typeof obj.then;
}

/**
 * Check if `obj` is a generator.
 *
 * @param {Mixed} obj
 * @return {Boolean}
 * @api private
 */

function isGenerator(obj) {
  return "function" == typeof obj.next && "function" == typeof obj.throw;
}

/**
 * Check if `obj` is a generator function.
 *
 * @param {Mixed} obj
 * @return {Boolean}
 * @api private
 */
 
function isGeneratorFunction(obj) {
  var constructor = obj.constructor;
  if (!constructor) return false;
  if ("GeneratorFunction" === constructor.name || "GeneratorFunction" === constructor.displayName) return true;
  return isGenerator(constructor.prototype);
}

/**
 * Check for plain object.
 *
 * @param {Mixed} val
 * @return {Boolean}
 * @api private
 */

function isObject(val) {
  return Object == val.constructor;
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/87020.html

相关文章

  • thunkify与co源码解读

    开头 首先本文有将近3000字,阅读可能会占用你20分钟左右。 文笔可能不佳,希望能帮助到阅读此文的人有一些收获 在进行源码阅读前首先抱有一个疑问,thunk函数是什么,thunkify库又是干什么的,co又是干嘛,它有啥用 程序语言有两种求值策略 传名调用 传入参数实际上是传入函数体 传值调用 函数体在进入的时候就进行运算计算值 编译器的传名调用实现,往往是将参数放到一个临时函数之中,再将这个...

    Tangpj 评论0 收藏0
  • Koa源码阅读笔记(1) -- co

    摘要:正好自己之前也想看的源代码,所以趁着这个机会,一口气将其读完。源码解读的源代码十分简洁,一共才两百余行。结语的源代码读取来不难,但其处理方式却令人赞叹。而且阅读的源代码,是阅读源码的必经之路。 本笔记共四篇Koa源码阅读笔记(1) -- coKoa源码阅读笔记(2) -- composeKoa源码阅读笔记(3) -- 服务器の启动与请求处理Koa源码阅读笔记(4) -- ctx对象 起...

    taoszu 评论0 收藏0
  • Koa源码阅读笔记(2) -- compose

    摘要:于是抱着知其然也要知其所以然的想法,开始阅读的源代码。问题读源代码时,自然是带着诸多问题的。源代码如下在被处理完后,每当有新请求,便会调用,去处理请求。接下来会继续写一些阅读笔记,因为看的源代码确实是获益匪浅。 本笔记共四篇Koa源码阅读笔记(1) -- coKoa源码阅读笔记(2) -- composeKoa源码阅读笔记(3) -- 服务器の启动与请求处理Koa源码阅读笔记(4) -...

    roland_reed 评论0 收藏0
  • Koa源码阅读笔记(3) -- 服务器の启动与请求处理

    摘要:本笔记共四篇源码阅读笔记源码阅读笔记源码阅读笔记服务器启动与请求处理源码阅读笔记对象起因前两天阅读了的基础,和中间件的基础。的前端乐园原文链接源码阅读笔记服务器启动与请求处理 本笔记共四篇Koa源码阅读笔记(1) -- coKoa源码阅读笔记(2) -- composeKoa源码阅读笔记(3) -- 服务器の启动与请求处理Koa源码阅读笔记(4) -- ctx对象 起因 前两天阅读了K...

    mrcode 评论0 收藏0
  • Codeigniter 4.0-dev 版源码学习笔记之一——前言以及 CI 4 预览

    摘要:版权声明可转载,但不论任何媒体都需要在转载前与本人沟通,并在转载时注明出处。的各个核心模块以模块名为目录名分别存储在这个目录下。下一篇文章会涉及到和。此文可以转载,但转载前需要发邮件到进行沟通,未沟通的均视作侵权。 写在前面: 为什么选择开发过程中的 CI 4 作为源码解读版本:(1)首先我选 CI 是因为它之前的稳定版都是相对比较轻量小巧的,而且可以认为是简单的。(2)为什么没有选...

    MSchumi 评论0 收藏0

发表评论

0条评论

xavier

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<