async给nodejs开发者们提供了很好的异步转同步的方法, 其中就以waterfall的应用最为广泛,waterfall的典型用法如下:
var async = require("async");
async.waterfall(
[
//step1
function (cb) {
console.log("step1");
cb(null);
},
//step2
function (cb) {
console.log("step2");
cb(null);
},
//step3
function (cb) {
console.log("step3");
cb(null);
}
],
function (e) {
console.log("complete, ", e);
}
);
这段代码以可控顺序输出:
step1
step2
step3
complete
但是有时候我们有这种需求:当step2执行出错时,我想重试最多两次step2,如果还出错,才跳到complete,而不是一出错就直接跳到complete。对于这种需求我是这么处理的:
var async = require("async");
var retrystep = 0;
async.waterfall(
[
//step1
function (cb) {
console.log("step1");
cb(null);
},
//step2
// 首先需要给step2函数命名,不能是匿名函数
function step2(cb) {
console.log("step2, retry=", retrystep);
//这里执行实际的逻辑过程,返回result
var result = false;// do_something();
//如果result错误,且重试次数小于3次,则继续执行step2
if (!result && retrystep < 2) {
//记录下增加的重试次数
retrystep++;
//再次执行step2,注意要带上参数cb
step2(cb);
}
else {
//如果执行正确就下一步
if (result) {
cb(null);
}
//否则就直接跳到结束complete
else {
cb("step2 error");
}
}
},
//step3
function (cb) {
console.log("step3");
cb(null);
}
],
//complete
function (e) {
console.log("complete, ", e);
}
);
这段代码输出:
step1
step2, retry= 0
step2, retry= 1
step2, retry= 2
complete, step2 error
step2执行了3次,这是重试自己这一步骤的情况,但是如果我重试上一步骤怎么办呢?根据上面的方法,我们很自然想到在step2中调用step1函数,如下:
var async = require("async");
var retrystep = 0;
async.waterfall(
[
//step1
function step1(cb) {
console.log("step1");
cb(null);
},
//step2
// 首先需要给step2函数命名,不能是匿名函数
function step2(cb) {
console.log("step2, retry=", retrystep);
//这里执行实际的逻辑过程,返回result
var result = false;// do_something();
//如果result错误,且重试次数小于3次,则继续执行step2
if (!result && retrystep < 2) {
//记录下增加的重试次数
retrystep++;
//再次执行step2,注意要带上参数cb
step1(cb);
}
else {
//如果执行正确就下一步
if (result) {
cb(null);
}
//否则就直接跳到结束complete
else {
cb("step2 error");
}
}
},
//step3
function (cb) {
console.log("step3");
cb(null);
}
],
//complete
function (e) {
console.log("complete, ", e);
}
);
但是这段代码是有问题的,首先step2函数里面是调用不到step1函数的,其次调用step1的参数cb并不是step1的参数,所以要解决这个问题,我们就要设法在step2里面可以得到step1和其参数,我是这么实现的:
var async = require("async");
var retrystep = 0;
var state = {};
async.waterfall(
[
//step1
//同样step1需要命名
function step1(cb) {
//这里记录下step1函数名和参数,保存在state中,用于在每个步骤中传递
state.step1 = step1;
state.step1cb = cb;
console.log("step1, retry=", retrystep);
cb(null);
},
//step2
// 首先需要给step2函数命名,不能是匿名函数
function step2(cb) {
console.log("step2, retry=", retrystep);
//这里执行实际的逻辑过程,返回result
var result = false; // do_something();
//如果result错误,且重试次数小于3次,则再次从step1执行起
if (!result && retrystep < 2) {
//记录下增加的重试次数
retrystep++;
//再次从step1执行起来,同样要带上保存的参数step1cb
state.step1(state.step1cb);
}
else {
//如果执行正确就下一步
if (result) {
cb(null);
}
//否则就直接跳到结束complete
else {
cb("step2 error");
}
}
},
//step3
function (cb) {
console.log("step3");
cb(null);
}
],
//complete
function (e) {
console.log("complete, ", e);
}
);
以上这段代码输出如下:
step1, retry= 0
step2, retry= 0
step1, retry= 1
step2, retry= 1
step1, retry= 2
step2, retry= 2
complete, step2 error
step1和step2各执行了3次。