js 异步流程控制之 avQ(avril.queue)
2015-01-17 23:24 flowforever 阅读(1363) 评论(0) 编辑 收藏 举报
废话前言
写了多年的js,遇到过最蛋疼的事情莫过于callback hell, 相信大家也感同身受。
业界许多大大也为此提出了很多不错的解决方案,我所了解的主要有:
- 朴灵 event proxy, 简单明了容易上手
- 老赵的 wind.js, 写起来最舒坦,最能表达程序顺序执行逻辑
- Promise,个人感觉为解决一个坑引入另外一个坑,写出来的代码一大坨,代码可读性最差
我这人闲着没事也爱折腾,我也自己造轮子,不为别的只为自己代码写的舒服流畅.
传送门:目前只支持 node.js 环境,以后有时间再增加浏览器支持
- github https://github.com/flowforever/avril.queue
- npm install avril.queue
简介
为啥叫avQ: 全称是avril.queue因为哥喜欢avril就用她名字命名,跟岛国无关
核心:avQ作为一个异步流程控制框架包含两大要素来实现异步流程控制并且编码方便流畅
- queue 执行队列 确保程序逻辑按照顺序执行
- $AwaitData 异步等待数据对象,该对象可以作为参数用于下一步异步请求,实现编码流畅的核心
动手avQ
Basic example: q.func, q.$await, q.$each
var q = avQ();
var $fileContent = q.$await(fs.readFile, 'the/path/of/file.txt', function(err, res){ /*此时的this 并不是指向q,而是一个新的avQ()对象 所以this实际上就是一个subQueue, this.error()会将error层层上抛默认只上抛不做任何处理,以后章节我会详细介绍q.func执行的原理*/ this.error(err); return res; });
如果fs.readFile 需要上下文怎么办
q.$await, q.$each, 以及后面的q.$$await,q.$$each 都支持传入context作为第一个参数,例如
var $fileContent = q.$await(fs, fs.readFile, 'the/path/of/file.txt', function(err, res){ this.error(err); return res; }); var $$fileContent = q.$$await(fs, fs.readFile, 'the/path/of/file.txt'); // 会自动将 res 最为 $awaitData 的结果 var $fileExisted = q.$await(fs.exists, 'the/path/of/file.txt'); // 默认将返回fs.existe 的 callback(existed) 中第一个参existed 数作为值
var avQ = require('avril.queue'); var q = avQ();
/* return $AwaitData object */ var $fileContent = q.$await(fs.readFile, 'the/path/of/file.txt' , function(err, fileContent){ this.error(err); return fileContent; }); /* convert the $awaitData' result , return a new $AwaitData object */ var $ids = $fileContent.convert(function($org){ return $org.result().split('\n'); }); /* return $AwaitData object which result is list of */ var $users = q.$each(db.User.findById, $ids, function(err, user){ this.error(err); return user; }); q.func(function(){ console.log( $fileContent.result() ) console.log( $users.realResult() ); });
Syntax Sugar: q.$$await, q.$$each,
q.$if($awaitData, trueFunc), q.$if($awaitData, trueFunc).$else(), q.$if($awaitData, trueFunc).$elseIf($otherAwaitData, otherTrueFunc)
对于标准的node.js异步调用一般返回值是callback(err, result),
var $fileContent = q.$$await(fs.readFile, 'the/path/of/file.txt'); // 跟$await() 的区别在于$await 最后一个参数需要callback: return res;
$fileContent在异步请求完毕之后将会自动使用result 作为结果.
var avQ = require('avril.queue'); var q = avQ(); var $fileContent, $ids, $users; /* return $AwaitData object */ var filePath1 = 'the/path/of/file1.txt'; var filePath2 = 'the/path/of/file2.txt'; var $fileExisted = q.$await(fs.exists, filePath1); q.$if($fileExisted, function(){ $fileContent = this.$$await(fs.readFile, filePath1); }).$else(function(){ $fileContent = this.$$await(fs.readFile, filePath2); }); q.func(function(){ /* convert the $awaitData' result , return a new $AwaitData object */ $ids = $fileContent.convert(function($org){ return $org.result().split('\n'); }); /* return $AwaitData object which result is list of */ $users = this.$$each(db.User.findById, $ids); }); q.func(function(){ console.log( $fileContent.result() ) //只取出第一级 result() console.log( $users.realResult() ); // 递归取出所有的result() });
小结
人无完人,码无完码,欢迎大家拍砖,如果大家有兴趣我会继续推出更多api文档及介绍(大家没兴趣还是要继续写完整,哈哈).
大家要觉得我这个库有意思不妨给个github start