nodeJS:在瀑布中运行一批子进程命令

时间:2014-01-12 17:43:25

标签: node.js git child-process

我正在尝试优雅地一个接一个地运行五个git命令,同时保持捕获错误和进度的能力:

  • git status
  • git pull
  • git add。
  • git commit -am“commit message”
  • git push

开源说明:我研究过不同的node-git库,并决定自己实现它。

使用Q,我创建了一个延迟方法来运行子进程:

var exec = require('child_process').exec,
    path = require('path'),
    Q = require('q'),
    gitPath = path.resolve(__dirname + "/../projects/langs");


function run(command) {

    var deferred = Q.defer();

    exec(command, {cwd: gitPath}, function puts(error, stdout, stderr) {

        if (error) {
            deferred.reject(new Error(error));
        } else {
            deferred.resolve(stdout);
        }
    });

    return deferred.promise;
}

但是,我想避开Pyramid of doom

function option1() {
    // Pyramid of doom

    run("git status").then(function (output) {
        console.log(output);

        run("git pull").then(function (output) {
            console.log(output);

            run("git add .").then(function (output) {
                console.log(output);   
                // etc.    
            });

        });
    });
}

感觉不太优雅:

function options1a() {
    // Pyramid of doom

    run("git status").then(function (output) {
        console.log(output);

        run("git pull");
    }).then(function (output) {
            console.log(output);
            run("git add .")
        }).then(function (output) {
            console.log(output);

        });
}

我看到了第三种选择,但似乎无法让它发挥作用:

function promiseWaterfall(tasks) {
    var resolvedPromise = Q(undefined);

    var finalTaskPromise = tasks.reduce(function (prevTaskPromise, task) {
        return prevTaskPromise.then(task);
    }, resolvedPromise);  // initial value

    return finalTaskPromise;
}

promiseWaterfall([
    run("git status"),
    run("git pull"),
    run("git add .")
]).then(function () {
        console.log(arguments);
    });

我正在玩第四个使用async库的选项:

async.waterfall([
    function(callback){
        callback(null, 'one', 'two');
    },
    function(arg1, arg2, callback){
        callback(null, 'three');
    },
    function(arg1, callback){
        // arg1 now equals 'three'
        callback(null, 'done');
    }
], function (err, result) {
    // result now equals 'done'    
});

但这似乎让我走向了一条不承诺的道路。

如何让它优雅地工作?任何最佳做法?

1 个答案:

答案 0 :(得分:2)

我熟悉when.js promises,所以我将使用该承诺库回答您的问题。它提供了类似于基于回调的异步lib这类东西的辅助函数。查看他们的API documentation了解更多示例。

在下面的代码中,我使用when/sequence模块来执行您要查找的内容。我还修改了你的代码组织,以保持模块化(例如,不在你的示例中将git cwd嵌入到run函数中)。

这是一个完全有效的实施方案。确保将git cwd更改为您自己的git存储库,因为它当前指向我自己的存储库。

var exec = require('child_process').exec
, when = require('when')
, sequence = require('when/sequence');

// simple promise wrapper for exec
function exec_p(command, options) {
    options = options || {};
    var defer = when.defer();
    exec(command, options, function(error, stdout, stderr) {
        return error 
            ? defer.reject(stderr + new Error(error.stack || error))
            : defer.resolve(stdout);
    });
    return defer.promise;
}

// Some simple git wrapper
function Git(config) {
    var self = this;
    self.config = config;
    return function(gitCommand) {
        return exec_p('git ' + gitCommand, self.config);
    };
}

// create a new instnace of git and specify our options
var git = new Git({ cwd: "/home/trev/git/tsenior" });

// we can now use sequence & our newly created git wrapper to easily
// can things in order one after another
sequence([
    function() { return git('status'); },
    function() { return git('status'); },
    function() { return git('status'); },
    function() { return git('status'); }
]).then(function(results) { // handle the results here
    console.log(results);
}).otherwise(function(error) { // handle any errors here
    console.error(error.stack || error);
    process.exit(1);
});

每个步骤后提供的代码都不是console.log(它只是在结尾处注销结果),但可以很容易地修改它来执行此操作。