`

co模块源码

    博客分类:
  • node
 
阅读更多

1.co处理延迟实现

 

  • 整体回调的实现:借用生成器函数执行完成后又有generator.done标志执行完成,因此可以创建一个新的Promise对象wrapPromise作为返回值,然后调用wrapPromise的then、catch等方法注册回调函数。生成器函数执行完毕时,generator.done标志为true,调用resolve函数,以触发resolved状态下的回调函数得到执行;当生成器函数执行过程中捕获错误时,调用reject函数,以触发rejected状态下的回调函数得到执行。同类Promise处理失败回调时需要反复注册onRejected回调函数。
  • 延迟函数的实现:借用生成器函数执行时需要调用generator.next方法逐段执行的原理,将generator.next返回值设置拥有延迟特征的Promise对象stepPromise,stepPromise的回调函数添加为generator.next,即可以在stepPromise延迟执行完毕后,再次执行生成器的代码段。因此同步函数在生成器中执行,异步函数在Promise中执行。同类功能,Promise模块的实现是要反复调用promise.then方法注册onFulfilled回调函数。
// 参数fn是生成器函数,调用co.call方法返回Promise对象,不致产生多余的闭包
co.wrap=function (fn){
    createPromise.__generatorFunction__=fn;
    return createPromise;
    function createPromise(){
        return co.call(this,fn.apply(this,arguments));
    }
};

// 利用生成器逐段执行的特性,因此可以在延时执行完毕后调用gen.next方法(gen.next返回值需要是Promise对象)
// 简化对报错的处理,捕获到错误时执行reject函数触发wrapPromise的回调函数
function co(gen){// 首参为生成器function *(){}
    var ctx=this;
    var args=slice.call(arguments,1)

    // 延迟函数组最终成功执行完毕时调用resolve,失败时调用reject
    return new Promise(function(resolve,reject){
        if ( typeof gen==='function' ) gen=gen.apply(ctx,args);// 首参为普通函数,直接调用
        if ( !gen || typeof gen.next!=='function' ) return resolve(gen);

        onFulfilled();

        // 逐次调用gen.next执行生成器的各段yield、return前代码
        function onFulfilled(res){
            var ret;
            try{
                ret=gen.next(res);
            }catch(e){
                return reject(e);
            }
            next(ret);
        }

        // 延迟promise对象失败时调用,报错
        function onRejected(err){
            var ret;
            try{
                ret=gen.throw(err);
            }catch(e){
                return reject(e);
            }
            next(ret);
        }

        function next(ret){
            // 延迟函数组最终成功执行完毕时调用resolve,co执行完毕
            if (ret.done) return resolve(ret.value);

            var value=toPromise.call(ctx,ret.value);

            // value必须是带有延迟性质的promise对象,该延迟执行完成后调用onFulfilled或onRejected
            if (value && isPromise(value)) return value.then(onFulfilled,onRejected);

            return onRejected(
                // TypeError类型错误,gen.next返回值不能转化为Promise对象
                new TypeError('You may only yield a function, promise, generator, array, or object, '
                +'but the following object was passed: "'+String(ret.value)+'"')
            );
        }
    });
}

 

 

 2.保证generator.next返回Promise对象

 

  • toPromise(obj) 将数组、生成器、对象、thunk函数转化为Promise对象,Promise对象原样输出,其他类型也原型输出。因此返回值可能不是Promise对象,需要作校验。数组转化为Promise对象时,需要调用arrayToPromise函数;对象需要调用objectToPromise;thunk函数需要调用thunkToPromise;生成器直接调用co函数(koa中使用便是嵌套调用co函数)。

 

// 转化为promise对象
function toPromise(obj){
    if ( !obj ) return obj;

    if ( isPromise(obj) ) return obj;

    // 生成器对象使用co方法转化为promise对象
    if ( isGeneratorFunction(obj) || isGenerator(obj) )
    return co.call(this, obj);


    if ('function' == typeof obj) return thunkToPromise.call(this, obj);

    // 数组遍历以后递归调用toPromise方法转化为promise对象
    if ( Array.isArray(obj) ) 
    return arrayToPromise.call(this, obj);


    if (isObject(obj)) return objectToPromise.call(this, obj);
    return obj;
}

 

  • isPromise(obj) 判断obj的then属性是否函数,由此判断obj是否Promise对象。欠缺之处是没法排除其他带有then方法的对象,需要使用者的心领神会。

 

// 通过then方法是否存在判断是否promise
function isPromise(obj){
    return 'function'==typeof obj.then;
}

 

  • arrayToPromise(obj) 将数组转化为Promise对象。在执行过程中,首先将数组的每个元素项都通过调用toPromise函数转化为Promise对象,然后调用Promise.all方法将各Promise方法合并为一个,回调的触发时机为最末一个延迟Promise对象执行完毕时。

 

// 遍历后递归调用toPromise方法转化为promise对象
function arrayToPromise(obj){
    return Promise.all(obj.map(toPromise,this));// Promise.all获取数组中所有promise对象
}

 

  • thunkToPromise(obj) 将thunk函数(thunk的参数为函数call)转化为Promise对象。实现原理是将resolve、reject函数传入call中执行,因此thunk函数执行过程中也就能调用Promise对象的回调函数了。

 

// thunk参数为待执行函数,将resolve、reject回调启动函数传给待执行函数
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);
        });
    });
}

 

  • objectToPromise(obj)将对象转化为Promise对象。执行过程中,首先以obj对象的构造函数生成对象results,接着尝试obj对象的属性转化为Promise对象,假若能够转化为Promise对象,该Promise对象回调执行完成时,将回调函数的参数存入results的对应属性中;假若不能转化为Promise对象,以obj对象的属性值存入results的对应属性中。各Promise对象又通过Promise.all方法合并到一个superPromise中,当最末一个延迟对象执行完毕时,执行superPromise的回调函数,返回results。

 

// obj属性能转化成Promise对象,转化为Promise对象,results相应属性设为undefined,否则不转化存储在results中
// 每个Promise对象延时执行完毕后更新results,results作为参数传给Promise.all的回调,作为返回值
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;
        }));
    }
}
 

 

3.源码

 

var slice=Array.prototype.slice;

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

// 参数fn是生成器函数,调用co.call方法返回Promise对象,不致产生多余的闭包
co.wrap=function (fn){
    createPromise.__generatorFunction__=fn;
    return createPromise;
    function createPromise(){
        return co.call(this,fn.apply(this,arguments));
    }
};

// 利用生成器逐段执行的特性,因此可以在延时执行完毕后调用gen.next方法(gen.next返回值需要是Promise对象)
// 简化对报错的处理,捕获到错误时执行reject函数触发wrapPromise的回调函数
function co(gen){// 首参为生成器function *(){}
    var ctx=this;
    var args=slice.call(arguments,1)

    // 延迟函数组最终成功执行完毕时调用resolve,失败时调用reject
    return new Promise(function(resolve,reject){
        if ( typeof gen==='function' ) gen=gen.apply(ctx,args);// 首参为普通函数,直接调用
        if ( !gen || typeof gen.next!=='function' ) return resolve(gen);

        onFulfilled();

        // 逐次调用gen.next执行生成器的各段yield、return前代码
        function onFulfilled(res){
            var ret;
            try{
                ret=gen.next(res);
            }catch(e){
                return reject(e);
            }
            next(ret);
        }

        // 延迟promise对象失败时调用,报错
        function onRejected(err){
            var ret;
            try{
                ret=gen.throw(err);
            }catch(e){
                return reject(e);
            }
            next(ret);
        }

        function next(ret){
            // 延迟函数组最终成功执行完毕时调用resolve,co执行完毕
            if (ret.done) return resolve(ret.value);

            var value=toPromise.call(ctx,ret.value);

            // value必须是带有延迟性质的promise对象,该延迟执行完成后调用onFulfilled或onRejected
            if (value && isPromise(value)) return value.then(onFulfilled,onRejected);

            return onRejected(
                // TypeError类型错误,gen.next返回值不能转化为Promise对象
                new TypeError('You may only yield a function, promise, generator, array, or object, '
                +'but the following object was passed: "'+String(ret.value)+'"')
            );
        }
    });
}

// 转化为promise对象
function toPromise(obj){
    if ( !obj ) return obj;

    if ( isPromise(obj) ) return obj;

    // 生成器对象使用co方法转化为promise对象
    if ( isGeneratorFunction(obj) || isGenerator(obj) )
    return co.call(this, obj);


    if ('function' == typeof obj) return thunkToPromise.call(this, obj);

    // 数组遍历以后递归调用toPromise方法转化为promise对象
    if ( Array.isArray(obj) ) 
    return arrayToPromise.call(this, obj);


    if (isObject(obj)) return objectToPromise.call(this, obj);
    return obj;
}

// thunk参数为待执行函数,将resolve、reject回调启动函数传给待执行函数
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);
        });
    });
}

// 遍历后递归调用toPromise方法转化为promise对象
function arrayToPromise(obj){
    return Promise.all(obj.map(toPromise,this));// Promise.all获取数组中所有promise对象
}

// obj属性能转化成Promise对象,转化为Promise对象,results相应属性设为undefined,否则不转化存储在results中
// 每个Promise对象延时执行完毕后更新results,results作为参数传给Promise.all的回调,作为返回值
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;
        }));
    }
}

// 通过then方法是否存在判断是否promise
function isPromise(obj){
    return 'function'==typeof obj.then;
}


// 通过next、throw方法是否存在判断是否生成器
function isGenerator(obj){
  return 'function'==typeof obj.next && 'function'==typeof obj.throw;
}

// 通过构造器是否GeneratorFunction或GeneratorFunction、isGenerator判断是否生成器
function isGeneratorFunction(obj){
    var constructor=obj.constructor;
    if (!constructor) return false;
    if ( 'GeneratorFunction'===constructor.name || 'GeneratorFunction'===constructor.displayName ) 
        return true;
    return isGenerator(constructor.prototype);
}

// 判断是否普通对象
function isObject(val){
    return Object==val.constructor;
}

 

 

 4.koa中间件使用co,以koa-static,静态文件中间件为例

 

  • 整体处理koa(require("koa-compose")(this.middleware)) // koa/lib/application.js
  • koa-static执行文件中返回生成器函数,使用yield* next执行下一个中间件
'use strict';

const resolve=require('path').resolve;
const assert=require('assert');
const debug=require('debug')('koa-static');
const send=require('koa-send');

module.exports=serve;

// root静态文件根目录
function serve(root,opts){
    opts=opts || {};

    assert(root,'root directory is required to serve files');

    // options
    debug('static "%s" %j', root, opts);
    opts.root=resolve(root);
    if ( opts.index!==false ) opts.index=opts.index || 'index.html';

    if ( !opts.defer ){// 预先响应静态文件,随后执行下一个中间件,默认
        return function *serve(next){
            if (this.method == 'HEAD' || this.method == 'GET') {
                if ( yield send(this,this.path,opts) ) return;
            }
            yield* next;// 在生成器函数中执行另一个生成器
        };
    }

    return function *serve(next){// 预先执行下一个中间件,随后响应静态文件
        yield* next;

        if ( this.method!='HEAD' && this.method!='GET' ) return;
        // response is already handled
        if ( this.body!=null || this.status!=404 ) return;

        yield send(this,this.path,opts);
    };
}

 

  • koa-compose中间件处理器,将中间件合并为单一的含有多个yield的函数,yield语法调用下一个中间件。
module.exports=compose;

// 合并中间件为单一的包含多个yield的函数
function compose(middleware){
  	return function *(next){
	    if (!next) next=noop();

	    var i=middleware.length;

	    // 逆序执行中间件,将下一个中间件添加当前中间件的返回值中
	    while ( i-- ){
	      	next=middleware[i].call(this,next);
	    }

	    return yield *next;
  	}
}

function *noop(){}

 

  • koa-compose处理koa-static结果。

 

function *(){
	// opts等来自koa-static函数
    if (this.method == 'HEAD' || this.method == 'GET') {
        if ( yield send(this,this.path,opts) ) return;
    };

    yield* next;// next为noop空函数构成的生成器
}
 

 

  • co.wrap对结果作最后处理,顺序执行延迟

 

  • co.7z (6.4 KB)
  • 下载次数: 0
分享到:
评论

相关推荐

    SAP CO模块培训

    SAP CO模块是SAP企业资源规划系统中的一个重要组成部分,主要负责成本控制和管理。这个模块提供了详尽的成本计算和分析工具,帮助企业更好地理解和优化其成本结构。以下将详细阐述SAP CO模块的关键知识点。 首先,...

    SAP培训--CO模块学习CO的好材料

    公司代码是SAP系统中财务会计的基本单位,而成本控制范围则是CO模块的管理边界,它可以包含一个或多个公司代码。成本中心是成本发生的具体地点,可以是部门、生产线或任何成本发生的地方。成本中心组则用于管理和...

    MM,SD,PP,FI,CO等几个模块常用的table ABAP SAP

    ### CO模块常用Table 1. **CJ01N**:存储成本中心的预算数据,包括成本中心、会计年度、预算额等。 2. **CJI3**:存储成本中心的实际成本数据,记录实际发生的成本或费用。 3. **KPE1**:存储利润中心的业绩数据,...

    co模块

    标题 "co模块" 提到的是一个在Node.js中常用的异步流程控制库,它主要用于处理ES6的Generator函数,使得异步代码可以按照同步的方式编写,提高了代码的可读性和可维护性。在这个主题下,我们可以深入探讨以下几个...

    CO传感器模块资料

    描述中提到“CO传感器的使用资料,没有源代码,使用时主要的AD采集,采用模拟口”,这意味着这份资料可能包含如何操作和解读CO传感器数据的信息,但不包括具体的编程代码。AD采集是指将传感器采集到的模拟信号转换为...

    SAP 成本模块讲义

    成本控制范围是 SAP CO 模块中的一个重要概念,是指公司代码与控制区域的关系。成本控制范围可以分为两种:一种是 One Controlling Area is Assigned to One Company Code,另一种是 Multiple Company Codes ...

    SAP-CO模块成本中心会计配置及操作详解.pdf

    SAP CO模块成本中心会计配置及操作详解 SAP公司提供的企业资源计划软件,帮助企业整合和管理业务流程。SAP CO模块作为其核心组件之一,涵盖了多个子模块,其中成本中心会计(Cost Center Accounting)允许企业进行...

    SAP模块常用事务代码.pdf

    本文档总结了 SAP FI-CO 模块中常用的事务代码,涵盖了科目主数据维护、总帐科目凭证处理、预制凭证审核、科目分配模型清帐、财务报表生成、外币评估、GR/IR 清账等业务流程。 科目主数据维护 在 SAP FI-CO 模块中...

    SAP CO葵花宝典

    - **次级成本要素**:只存在于CO模块中,用于辅助成本分配和成本计算过程,不会直接影响FI模块中的总账。 #### 二、具体会计科目及成本要素示例分析 以下是一些具体的会计科目和成本要素示例及其用途: 1. **...

    SAP各模块事务码 CO模块

    这是本人在用SAP的时候总结下来的事务码,希望对学习SAP的朋友有帮助.

    SAP-FI-CO-模块常用事务代码.docx

    SAP FI-CO 模块常用事务代码 SAP FI-CO 模块是 SAP 系统中的一部分,负责财务会计和成本控制。该模块提供了许多事务代码,用于执行各种财务操作。下面是常用的事务代码: 科目主数据维护 * FS00: 科目主数据维护 ...

    CAD中常用模块代码

    例如,M(移动)、RO(旋转)、SC(缩放)、CO(复制)、TR(修剪)、EX(延伸)等代码。 3. **图层管理模块**:CAD中的图层就像绘画的调色板,可以分别控制不同元素的显示、颜色、线型和线宽。图层管理代码用于...

    SAP-CO成本管理模块功能介绍.pptx

    SAP CO模块是SAP系统中的成本管理模块,主要负责企业内部的成本计算、控制和分析。这个模块的设计目的是为了帮助企业更好地理解和管理其运营成本,从而作出更明智的决策。以下是对SAP CO模块功能的详细解释: **第...

    SAP知识文档-CO.pdf

    - **解决方案一**: 通过CO模块将费用转移到其他成本中心。 - **解决方案二**: 通过FI模块更改会计凭证中的成本中心。 **3. 成本中心货币设置** - 在控制范围设置中,若将公司代码-&gt;CO范围设置为2(跨公司成本核算)...

    SAPFICO模块基本设置.docx

    2. 公司代码设置:在 SAP FI/CO 模块中,公司代码是最基本的结构单元。用户需要定义公司代码、公司名称、公司地址、语言、国家、货币等信息。 知识点:公司代码设置涉及到公司代码、公司名称、公司地址、语言、国家...

    CO-OFDM程序代码

    "CO-OFDM程序代码"很可能是一个用于模拟或实现CO-OFDM系统的MATLAB程序。`CoOFDM.m`文件可能是这个程序的核心部分,包含了OFDM信号的生成、调制、传输以及解调等关键步骤的算法。下面将详细介绍这些关键步骤及其背后...

    SAP常用事务代码,涉及到MM板块

    这些事务代码覆盖了SAP的多个模块,包括财务(FI)、物料管理(MM)、销售与分销(SD)、生产计划(PP)等。以下是一些常用的SAP事务代码: 财务(FI)模块 FB01:创建会计凭证 FB02:修改会计凭证 FB03:查看会计...

Global site tag (gtag.js) - Google Analytics