要说什么啊: 一个和 co类似的模块,带点golang的感觉。
哦,原来是造轮子啊? 哎呀,客官,别急,简单看看呗,绝对大有不同 ^_^
发这儿干嘛: 分享,然后求意见。正是由于身边人的建议,更新到了现在的版本。
talk is cheap,show me the code
好,代码来了
defer
defer 这个功能借鉴至golang的defer 关键字,执行逻辑也一样,在将要退出时执行绑定的操作.
const co = require("zco");
const request = require("request");
co(function*(next,defer) {
defer(function*(inner_next,error) {
if(error) {
console.log(error.message);//"模拟一下未知的错误" ,这个错误是后面手动throw模拟的
}
//做些清理工作,案例:个人在一个有热更新需求的项目中需要确保释放了模块的引用,并且无论之后的代码是否报错.
});
let [err,response,body]=yield request("www.baidu.com",next);//异步请求百度首页,与request模块无缝对接
//..... do something else
throw new Error("模拟一下未知的错误");
})();
inner_next功能和外部的next 一致,error是捕获的defer之后的代码抛出的异常。
超时终止协程
某些场景会设置最大等待时间,个人是做爬虫工作的,一些破网站经常超时,希望在超时时终止协程,不再继续往后执行。
到截止时间,还没有执行的操作就没有任何执行的意义了,应该尽早终止,这样避免了没有必要的资源消耗(内存,CPU,带宽…)。 需要注意的是 defer定义的操作在挂起协程时会执行.
来个栗子:
const co=require("zco");
let variable=1;
//先伪造一个耗时操作
const I_need_more_time=function(){
return co(function*(next){
variable+=100;//加100
yield setTimeout(next,2*100);//等待200毫秒,模拟耗时操作
variable+=1000;//加1000
})
}
//期望在100毫秒内返回结果,否则终止,并抛出超时错误。这里铁定超时
co.timeLimit(1*100,co(function*(){
variable+=10;
yield I_need_more_time();//执行准备好的耗时操作
variable+=10000;
return variable;
}))((err,result)=>{
if(err){
console.log(err.message);//打出 "timeout"
}
})
//设一个更长的等待,确保上面的耗时操作都能执行完
setTimeout(function(){
console.log(variable);//打印一下variable这个变量的值。
// 值为 "111" ,说明加1000和加10000的操作都没有执行,因为被终止执行了。
},400);
终止协程的方法是zco模块内部使用的,如果开发者因为其他原因想要终止协程,也可以使用,给个例子:
const co=require("zco");
var zco_future=co(function*(next){
//............
});
zco_future();//执行协程
zco_future.__zco_suspend__();//终止协程
只要持有zco的future就可以在任意时刻终止, co(), co.all(),co.timeLimit().co.wrapPromise() 都会返回一个zco的future。但是co.wrapPromise返回的future的终止方法并没有任何用,象征性的调一下,原因在于无法干预Promise内部的逻辑(有可能可以hack进去,还没研究过)。所以不推荐使用Promise。
结束语
上面列出的俩个特性是zco不同于tj 的co的主要区别。 写zco最初的目的是想抛弃Promise,因为在项目中Promise增加了额外的代码量,并且非常不喜欢Promise处理异常的方式,然而现存的coroutine模块又依赖于Promise,只好自己搞一个了。
在大家的建议和项目的需求中慢慢完善了这个模块,除了上述的示例,更多的例子可以在项目首页看到,地址: https://github.com/yyrdl/zco
欢迎提意见和建议 :)