NodeJS异步的特性有时候会导致程序非常的难看,回调一层套着一层,这个时候就要用流程控制模块来控制究竟是同步还是异步了。
Nimble是一个轻量、可移植的函数式流程控制模块。经过最小化和压缩后只有837字节,可以运行在Node.js中,也可以用在各种浏览器中。它整合了underscore和async一些最实用的功能,并且API更简单。
nimble有两个流程控制函数,_.parallel和_.series。顾名思义,我们要用的是第二个,可以让一组函数串行执行的_.series。下面这个命令是用来安装Nimble的:
npm install nimble
如果用.series调度执行上面那个解方程的函数,代码应该是这样的:
...
var flow = require('nimble');
(function calculate(i) {
if(i === l-1) {
variables[i] = res[i];
process.exit();
}else {
flow.series([
function (callback) {
calculateTail(res[i],res[i+1],function(tail) {
variables[i] = tail;
callback();
});
},
function (callback) {
calculateHead(res[i],res[i+1],function(head) {
res[i+1] = head;
callback();
});
},
function(callback){
calculate(i+1);
}]);
}
})(0);
...
.series数组参数中的函数会挨个执行,只是我们的calculateTail和calculateHead都被包在了另一个函数中。尽管这个用流程控制实现的版本代码更多,但通常可读性和可维护性要强一些。
更多实例:
串行执行(非异步):
var flow = require('nimble');
flow.series([
function (callback)
{
setTimeout(function()
{
console.log('I execute first.');
callback();
newfunc();
}, 1000)
},
function (callback)
{
setTimeout(function()
{
console.log('I execute next.');
callback();
}, 2000)
},
function (callback)
{
setTimeout(function()
{
console.log('I execute last.');
callback();
}, 100)
}
]);
并行执行(异步):
var flow = require('nimble');
flow.parallel([
function (callback) {
setTimeout(function () {
console.log('one');
callback();
}, 25);
},
function (callback) {
setTimeout(function () {
console.log('two');
callback();
}, 0);
}
]);
串并行兼顾(流程控制):
var flow = require('nimble');
var exec = require('child_process').exec;
function downloadNodeVersion(version, destination, callback)
{
var url = 'http://nodejs.org/dist/node-v' + version + '.tar.gz';
var filepath = destination + '/' + version + '.tgz';
exec('curl ' + url + ' > ' + filepath, callback);
}
flow.series
([
function(callback)
{
flow.parallel
([
function(callback)
{
console.log('Downloading Node v0.4.6...');
downloadNodeVersion('0.4.6', '/tmp', callback);
},
function(callback)
{
console.log('Downloading Node v0.4.7...');
downloadNodeVersion('0.4.7', '/tmp', callback);
}
], callback);
},
function(callback)
{
console.log('Creating archive of downloading files...');
exec
(
'tar cvf node_distros.tar /tmp/0.4.6.tgz /tmp/0.4.7.tgz',
function(error, stdout, stderr)
{
console.log('All Done!');
callback();
}
)
}
]);
这个稍微解释一下:先异步下载文件,文件下载完成后,再把所有文件打包。
nimble.min.js
(function(a){var b=Object.keys||function(a){var b=[];for(var c in a)a.hasOwnProperty(c)&&b.push(c);return b};var c=function(a,b){var c=Array.prototype[a];return function(d,e,f){var g=d?d[a]:0;return g&&g===c?g.call(d,e,f):b(d,e,f)}};var d=c("forEach",function(a,c){var d=a instanceof Object;var e=d?b(a):a||[];for(var f=0,g=e.length;f<g;f++){var h=d?e[f]:f;c(a[h],h,a)}});var e=function(a,c,e){var f=a.length||b(a).length;if(!f)return e();var g=0;d(a,function(){var a=function(a){a?(e(a),e=function(){}):++g===f&&e()};var b=Array.prototype.slice.call(arguments);c.length?(b=b.slice(0,c.length-1),b[c.length-1]=a):b.push(a),c.apply(this,b)})};var f=function(a,c,d){var e=b(a);if(!e.length)return d();var f=0;var g=function(){var b=e[f];var h=[a[b],b,a].slice(0,c.length-1);h[c.length-1]=function(a){a?(d(a),d=function(){}):++f===e.length?d():g()},c.apply(this,h)};g()};var g=c("map",function(a,b){var c=[];d(a,function(a,d,e){c[c.length]=b(a,d,e)});return c});var h=function(a){return function(b,c,d){var e=[];a(b,function(a,b,d,f){var g=function(a,b){e[e.length]=b,f(a)};var h=[a,b,d];c.length?(h=h.slice(0,c.length-1),h[c.length-1]=g):h.push(g),c.apply(this,h)},function(a){d(a,e)})}};var i=c("filter",function(a,b,c){var e=[];d(a,function(a,c,d){b(a,c,d)&&(e[e.length]=a)});return e});var j=function(a,b,c){var d=[];e(a,function(a,c,e,f){var g=function(b,c){c&&(d[d.length]=a),f(b)};var h=[a,c,e];b.length?(h=h.slice(0,b.length-1),h[b.length-1]=g):h.push(g),b.apply(this,h)},function(a){c(a,d)})};var k=c("reduce",function(a,b,c){d(a,function(a,d,e){c=b(c,a,d,e)});return c});var l=function(a,b,c,d){f(a,function(a,d,e,f){var g=function(a,b){c=b,f(a)};var h=[c,a,d,e];b.length?(h=h.slice(0,b.length-1),h[b.length-1]=g):h.push(g),b.apply(this,h)},function(a){d(a,c)})};a.each=function(a,b,c){return(c?e:d)(a,b,c)},a.map=function(a,b,c){return(c?h(e):g)(a,b,c)},a.filter=function(a,b,c){return(c?j:i)(a,b,c)},a.reduce=function(a,b,c,d){return(d?l:k)(a,b,c,d)},a.parallel=function(a,b){var c=new a.constructor;e(a,function(a,b,d){a(function(a){var e=Array.prototype.slice.call(arguments,1);c[b]=e.length<=1?e[0]:e,d(a)})},function(a){(b||function(){})(a,c)})},a.series=function(a,b){var c=new a.constructor;f(a,function(a,b,d){a(function(a,e){var f=Array.prototype.slice.call(arguments,1);c[b]=f.length<=1?f[0]:f,d(a)})},function(a){(b||function(){})(a,c)})}})(typeof exports==="undefined"?this._=this._||{}:exports)