co模块源码
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; })); } }
相关推荐
88254251 2020-11-01
bowean 2020-07-08
88520191 2020-07-05
Magicsoftware 2020-06-11
whynotgonow 2020-06-06
whynotgonow 2020-06-03
陈旭阳 2020-06-02
88520191 2020-05-20
89500297 2020-05-16
前端开发Kingcean 2020-04-16
nmgxzm00 2020-11-10
xixixi 2020-11-11
MarukoMa 2020-09-02
88234852 2020-09-15
陈旭阳 2020-08-31
whynotgonow 2020-08-19
前端开发Kingcean 2020-07-30
whynotgonow 2020-07-29