如何将Step代码转换为Async.JS(Step => waterfall,this.parallel)?

时间:2014-03-03 04:37:15

标签: javascript node.js async.js

几年前,我正在尝试使用NodeJS,发现"Step"库清理了我的一些代码。当希望更新代码时,我注意到Step上有一些“红旗”。 (几年没有更新,只有32次提交等)。

所以我环顾四周,发现Async.js,它具有更多功能和主动维护。

看起来很漂亮和一般。但是我开始尝试应用转换来代替使用它,并且可能没有采取正确的角度。

如果我正确阅读,Step的核心功能似乎是Async.JS将其称为"waterfall" pattern。所以在Step中你会写:

Step(
    function firstStepNoArgs() {
        foo.asyncCall(this);
    },
    function secondStep(err, argFromFoo) {
        if (err) {
            handleError(err);
        }

        bar.asyncCall(argFromFoo, 1, this.parallel());
        baz.asyncCall(argFromFoo, 2, this.parallel());
    },
    function thirdStep(err, argFromBar, argFromBaz) {
        if (err) {
            handleError(err);
        }

        /* etc... */
    }
);

如果我不知道更好,我猜你会在async.js中这样做(未经测试,考虑它是伪代码;我说的是理论上的变化我还没有真正追求过还)

function thirdStep(argFromBar, argFromBaz) {
    /* etc... */
}

async.waterfall([
    function firstStepNoArgs(callback) {
        foo.asyncCall(callback);
    },
    function secondStep(argFromFoo, callback) {
        async.parallel([
            barResult: function(callback) {
                bar.asyncCall(parameterFromFoo, 1, callback);
            },
            bazResult: function(callback) {
                baz.asyncCall(parameterFromFoo, 2, callback);
            }
        ],
            function(err, result) {
                if (err) {
                    handleError(err);
                } else {
                    thirdStep(result.barResult, result.bazResult);
                }
            }
    }
],
   function(err, result) {
       if (err) {
           handleError(err);
       } else {
           /* no-op? just assume third-step runs? */
       }
   }
);

步骤非常集中和顺序,我的小草案显示它在改编中变得混乱。我错过了什么吗?

所以我的问题是:将明确的Step代码转换为Async.JS的正确方法是什么?或者我选择了错误的库升级到?我不希望我的代码变得更加丑陋,但我也不想依赖于一个似乎有点“死”的库。 : - /

2 个答案:

答案 0 :(得分:1)

这里要小心你的回调。回调签名是:

callback(err, arg1, arg2 ...)

因此,如果您的foo.asyncCall用以下方式调用它:

callback(result1, result2)

然后整个异步将从那一点神秘地失败。正确的成功回调应该以null开头,例如

callback(null, result1, result2)

以下是更正后的代码:

function thirdStep(argFromBar, argFromBaz, callback) {
    /* etc... */
    callback(null, result);
}

async.waterfall([
    function firstStepNoArgs(callback) {

        // error callback("failed").  First args is not null means failed
        // in case of error, it just goes straight to function(err, result)

        foo.asyncCall(callback);
    },
    function secondStep(argFromFoo, callback) {

        // argFromFoo come from previous callback (in this case, result1)

        async.parallel([
            barResult: function(callback) {
                bar.asyncCall(parameterFromFoo, 1, callback);
            },
            bazResult: function(callback) {
                baz.asyncCall(parameterFromFoo, 2, callback);
            }
        ],
            function(err, result) {
                if (err) {
                    // in case of error you should do callback(error),
                    // this callback is from secondStep(argFromFoo, callback).

                    // this will pass to final function(err, result).

                    handleError(err);

                } else {

                    // you need to do callback(null) inside thirdStep
                    // if callback is not called, the waterfall won't complete

                    thirdStep(result.barResult, result.bazResult, callback);
                }
            }
    }
],
   function(err, result) {
       if (err) {
           handleError(err);
       } else {

           // everything is executed correctly, 
           // if any step failed it will gone to err.

       }
   }
);

答案 1 :(得分:1)

根据要求,实施您正在做的承诺。只需粘贴并运行,您就应该明白这一点。请记住,代码的上半部分是设置模拟功能,以便您可以更好地了解其工作原理。有人可能会告诉我,我应该把它作为一个要点,我也可以这样做。

var Q = require('q');

var foo ={},  bar ={}, baz = {};
//   let's mock up some of your objects with some asynch functions
//   using setTimeout for async completion
foo.asyncCall = function ( cb) {
    setTimeout(function(){ cb(null, 'promises'); },500);
};
bar.asyncCall = function ( arg1, arg2, cb) {
    setTimeout(function(){
        var result = arg1 + ' can be ' + arg2;
        cb(null, result);
    },1200);
};
//  going to add a will-always-fail function for example purposes
bar.asyncFailure = function (arg1, arg2, cb){
    setTimeout(function(){
        cb(new Error(arg1 +' offer decent error handling'), null);
    },2000);    // longer delay - simulate a timeout maybe
};

baz.asyncCall = function ( arg1, arg2, cb) {
    setTimeout(function(){
        var result = arg1 + ' are really ' + arg2;
        cb(null, result);
    },800);
};

//  set up promise-enbaled calls. Q.denodeify is an easy way to deal with any
//  standard node function with a final parameter being an (err,data) callback
//  If these are your own functions, you can also create your own promises, but
//  Q.nodeify is probably the fastest way to adapt existing code.

bar.promiseFailure = Q.denodeify(bar.asyncFailure);
bar.promiseCall = Q.denodeify(bar.asyncCall);
baz.promiseCall = Q.denodeify(baz.asyncCall);

//  this is your wrap up call ('thirdStep' in your code)
function allTogetherNow(arg1, arg2) {
    console.log(arg1 +'\n' + arg2);
};

// now we can have some fun
//  an example that will run to completion normally
//  Q.ninvoke is sort of a 'one-time' denodeify, it invokes a node-style function
//  and returns a promise

function example(){
    Q.ninvoke(foo,'asyncCall')
        .then( function (x) {
            return [bar.promiseCall(x, 'confusing at first'),
                    baz.promiseCall(x, 'awesome after that')]
        })
        .spread(allTogetherNow)
        .fail(function(e){console.log('Had an error')})
        .finally(function(){console.log('Calling no matter what from example')});

};
// sometimes things aren't entirely fun though, and there can be an error
function example2(){
    Q.ninvoke(foo,'asyncCall')
        .then( function (x) {
            return [bar.promiseFailure(x, 'confusing at first'),
                    baz.promiseCall(x, 'awesome after that')]
        })
        .spread(allTogetherNow)
        .fail(function(e){console.log(e)})
        .finally(function(){console.log('Calling no matter what from example2')});
};

example();
example2();

对于那些不想打扰它的人,发出的输出是:

promises can be confusing at first
promises are really awesome after that
Calling no matter what from example
[Error: promises offer decent error handling]
Calling no matter what from example2