天天看点

async waterfall步骤出错后的重试

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次。