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空函数构成的生成器
}
分享到:
相关推荐
这是本人在用SAP的时候总结下来的事务码,希望对学习SAP的朋友有帮助.
SAP-FI-CO-模块常用事务代码.docx
CO传感器的使用资料,没有源代码,使用时主要的AD采集,采用模拟口
OSCOMMERCE 2co 2checkout国外信用卡收款模块
SAP中的FI模块及CO模块的后台配置及注意点。
NULL 博文链接:https://schifred.iteye.com/blog/2318752
COSIO CO 2传感器 基于ESP8266和MH-Z19c NDIR CO 2模块的IoT CO 2传感器。 所需软件 带有PlatformIO扩展的VS代码或Atom 官方产品网站 建筑指南
程序源码甲烷浓度串口采集,CO浓度ADC采集,RTC时钟显示,STM32F103RCT6,正点原子mini板,7针SPI OLED显示。使用于煤矿井下甲烷浓度、CO浓度数据采集,防止井下瓦斯浓度过大。适用于工程开发技术人员、高校电子自动...
TI微型蓝牙模块设计,利用TI公司CC2541作为蓝牙模块,用作气体传感器用于检测CO浓度,其中包括ti源码源码,以及调试方法
基于51单片机的空气质量检测(温湿度,H2S,NH3,CH4,CO)proteus、原理图、流程图、物料清单、仿真图、源代码) 基于51单片机的空气质量检测(温湿度,H2S,NH3,CH4,CO) 面向污水处理厂的气体检测电子鼻系统硬件部分...
这是ICLR'21口头接受的“ Co-Mixup:显着性联合超模量联合混合代码”()。 部分代码是从Puzzle Mix()中借用的。 引用这项工作 @inproceedings{ kim2021comixup, title={Co-Mixup: Saliency Guided Joint Mixup ...
为了提高性能,SAP默认不开启修改日志功能,但是在日常操作中,经常会发现有人修改了生产订单的信息又不好查找当事人。 为了追溯工单的修改历史,找到责任人,那么就需要在工单修改后记录日志,便于查找。...
1.内容概要: 影响微生物共现网络的两个主要因素为:...R代码——phylogenetic properties of cluster in co-occurrence network.Rmd 结果展示——phylogenetic properties of cluster in co-occurrence network.html
热更新模块代码+配套脚本工具 3.0 更新日志: LoadScene更名为UpdateScene 更新文件大小提示(downloaded/total) 更新前预执行额外的lua代码preupdate/preupdate.lua, 可单独更新preupdate以避免更新整个updater模块 ...
用于CO模块成本对象控制的主数据 57 创建一个生产订单 58 在PP模块中创建一个生产订单:程序 59 计算一个生产订单的已计划成本 62 在CO 模块中创建一个生产订单 66 在订单相关生产中的一般费用 67 在订单相关生产里...
模块类似于一个封装库,从 Lua 5.1 开始,Lua 加入了标准的模块管理机制,可以把一些公用的代码放在一个文件里,以 API 接口的形式在其他地方调用,有利于代码的重用和降低代码耦合度。 Lua 的模块是由变量、函数等...
创建成本控制范围 ZT01公司决定激活SAP管理会计(CO)模块来加强公司内部核算和管理,管理会计中最主要的组织结构称为"成本控制范围",如果是一个集团企业,下属多个公司可以在一个成本控制范围内来进行管理 步骤:IMG-...
生物信息学中蛋白质功能模块挖掘COACH聚类分析算法