JS co 函数库的含义和用法实例总结
本文实例讲述了JSco函数库的含义和用法。分享给大家供大家参考,具体如下:
继续学习阮一峰老师异步编程四部曲之三:co
co在很早之前就听超哥讲过,说在node编程中大量用到,源码很简单,但是想法很强大。
让我有空抓紧了解下,前一段时间弄离职的事情,跑来跑去累的够呛。
现在终于一切回归正常了,还在拼命的适应新公司的节奏。只能趁周末继续学习了。
好了,不瞎扯了,回归主题,前两篇文章我们分别学习了Generator函数和Thunk方式的自动执行。
今天我们接着上次的思路学习使用co工具实现Generator函数的自动执行。
再次拿出上一节的示例:
varfs=require('fs');
varthunkify=require('thunkify');
varreadFile=thunkify(fs.readFile);
vargen=function*(){
varr1=yieldreadFile('/etc/fstab');
console.log(r1.toString());
varr2=yieldreadFile('/etc/shells');
console.log(r2.toString());
};
这段代码用于读取两个文件,怎么使用co工具来实现自动执行呢?
很简单:
varco=require('co');
co(gen);
上面代码中,只要把Generator函数传入co函数,就会自动执行。
co函数返回一个Promise对象,因此可以用then方法添加回调函数,当Generator执行结束就会触发回调:
co(gen).then(function(){
//success
});
这么看起来的话好像和我们之前的Thunk方式差不多啊,只是把run函数名改成了co
唯一的区别是多了一层Promise包装,那么co和Thunk到底有什么区别呢?
正如我们的猜测一样,co其实是将之前的两种自动执行方式(Thunk和Promise)结合到了一起,包装成一个库。
使用它的前提和比Thunk多了一种Promise的情况,也就是yield返回值必须是Thunk函数和Promise对象之一
实现原理:
之前我们在研究Thunk函数时的第一步是使用thunkify将readFile包装成Thunk函数:
今天我们研究Promise就需要把readFile包装成Promise对象,看代码:
varfs=require('fs');
varreadFile=function(fileName){
returnnewPromise(function(resolve,reject){
fs.readFile(fileName,function(error,data){
if(error)reject(error);
resolve(data);
});
});
};
vargen=function*(){
varf1=yieldreadFile('/etc/fstab');
varf2=yieldreadFile('/etc/shells');
console.log(f1.toString());
console.log(f2.toString());
};
接下来是我特别喜欢老师做的一件事,先用最原始的方式,手动执行一遍代码。
在我们日常开发的过程中,也可以参考这种方式。当逻辑复杂时,不妨先用最直白的方式把它实现,
然后再去发现里面的规则,去优化重构。又开始扯了,看一下手动执行的代码:
varg=gen();
g.next().value.then(function(data){
g.next(data).value.then(function(data){
g.next(data);
});
});
手动执行其实就是用then方法,层层添加回调函数。理解了这一点,就可以写出一个自动执行器:
functionrun(gen){
varg=gen();//开始执行
functionnext(data){
varresult=g.next(data);
if(result.done)returnresult.value;
result.value.then(function(data){
next(data);
});
}
next();
}
run(gen);
和Thunk函数的区别是,Thunk函数在执行成功后把next传给thunkify,让thunkify来帮忙执行next
这里的做法是吧next交给Promise,promise控制请求成功时执行next。区别只有这么一点。
分析了原理之后,我们来分析下co的源码:
co函数接受一个Generator参数,返回一个Promise对象
functionco(gen){
varctx=this;
returnnewPromise(function(resolve,reject){
});
}
在返回的Promise对象里面,先检查参数gen是否为Generator函数。如果是,就执行该函数,得到一个内部指针对象;
如果不是就返回,并将Promise对象的状态改为resolved。
functionco(gen){
varctx=this;
returnnewPromise(function(resolve,reject){
if(typeofgen==='function')gen=gen.call(ctx);
if(!gen||typeofgen.next!=='function')returnresolve(gen);
});
}
接着,co对next方法进行包装,使异常能够暴露出来:
functionco(gen){
varctx=this;
returnnewPromise(function(resolve,reject){
if(typeofgen==='function')gen=gen.call(ctx);
if(!gen||typeofgen.next!=='function')returnresolve(gen);
onFulfilled();
functiononFulfilled(res){
varret;
try{
ret=gen.next(res);
}catch(e){
returnreject(e);
}
next(ret);
}
});
}
最后就是next方法了:
functionnext(ret){
if(ret.done)returnresolve(ret.value);
varvalue=toPromise.call(ctx,ret.value);
if(value&&isPromise(value))returnvalue.then(onFulfilled,onRejected);
returnonRejected(newTypeError('Youmayonlyyieldafunction,promise,generator,array,orobject,'
+'butthefollowingobjectwaspassed:"'+String(ret.value)+'"'));
}
});
next方法最主要的一行:
if(value&&isPromise(value))returnvalue.then(onFulfilled,onRejected);
co的使用条件,每一次yield返回必须是Promise对象,当promist处理成功时再次执行onFulfilled
以此来达到自动执行的效果。
co还支持并发的异步操作,yield返回一个数组或者是支持遍历的对象即可:
//数组的写法
co(function*(){
varres=yield[
Promise.resolve(1),
Promise.resolve(2)
];
console.log(res);
}).catch(onerror);
//对象的写法
co(function*(){
varres=yield{
1:Promise.resolve(1),
2:Promise.resolve(2),
};
console.log(res);
}).catch(onerror);
至此,co的自动执行原理我们已经学习完成了,
其实本质上不是很难,只是涉及到的模块较多,思路比较灵活
对我的小容量大脑来说,现在的认识还只到90%
所以这篇文章里面自己的思想比较少,更多的还是在复述老师的思路。
最后贴上原文的地址:co函数库的含义和用法
感兴趣的朋友可以使用在线HTML/CSS/JavaScript代码运行工具:http://tools.jb51.net/code/HtmlJsRun测试上述代码运行效果。
更多关于JavaScript相关内容可查看本站专题:《JavaScript常用函数技巧汇总》、《javascript面向对象入门教程》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》及《JavaScript数学运算用法总结》
希望本文所述对大家JavaScript程序设计有所帮助。