使函数调用for循环同步

时间:2015-05-06 09:23:34

标签: javascript node.js

我在请求中获得了一个字符串数组。 每个字符串都包含要在本机shell上执行的命令。

var process = require('child_process'); 

function execCommand(req,res,callback){
 var params = req.params.list              //list is an array in the   request
 var result = '';
 var command = '';  
 for (var i = 0; i < params.length; i++) {
  command = params[i];
  cmd = process.exec(command);                        
  cmd.stdout.on('data', function(data){                       
   //push the shell output in variable result
  });
  cmd.on('close', function(code) {                            
   //convert the variable result to a valid JSON
  });
 }
 res.send(result);
};

所有命令的结果在result变量中混淆了。如何使for循环中的函数调用同步?

4 个答案:

答案 0 :(得分:5)

使用execSync而不是exec!

 for (var i = 0; i < params.length; i++) {
    command = params[i];
    result += process.execSync(command).toString();                        
 }

正如其他人所指出的,这可能不是一个好主意,因为它会阻止事件循环和正在进行的任何请求。如果正在执行的流程需要很短的时间才能完成,这是可以接受的,但不是这样。

这是一个控制for循环中异步函数流的简单模式...每次进程返回时,params_finished递增1。一旦完成的进程数等于响应发送的进程总数。

 for (var i = 0, total_params=params.length, params_finished=0; i < total_params; i++) {
    command = params[i];
    cmd = process.exec(command);                        
    cmd.stdout.on('data', function(data){                       
        //push the shell output in variable result
    });
    cmd.on('close', function(code) {                            
        //convert the variable result to a valid JSON
        params_finished++
        if(params_finished == total_params){
            res.send(result);
        }
    });
 }

答案 1 :(得分:2)

您可以使用execSync或...

您需要添加控制流程库以帮助进行异步调用,因此您可以选择以串行或并行方式运行您的功能:

答案 2 :(得分:0)

另一种方法是使用等效于循环内的sleep函数。如果你知道循环的每个实例需要多长时间。

function sleep(ms) {
      return new Promise(resolve => setTimeout(resolve, ms));
    }

async function myLoopFunction(){
    // any code before loop
    for (var i = 0; i < params.length; i++) {
       // do whatever
       await sleep(3000); // 3000 milisecond, for instance
    }
};

有关此方法如何运作的完整说明,请参阅this answer

答案 3 :(得分:0)

将此功能用于 in serie synchronous for loop

function eachSeries(array, fn) {
    return new Promise(function (resolveGlobal, rejectGlobal) {

        var promises = []
        var next = 0

        fn(array[next], resolveObj, rejectObj)

        function resolveObj(data) {

            promises.push( Promise.resolve(data) )

            next++

            if (next >= array.length) {
                Promise.all(promises).then(function (data) {
                    resolveGlobal(data)
                }).catch(function (error) {
                    rejectGlobal(error)
                })
            } else {
                fn(array[next], resolveObj, rejectObj)
            }
        }

        function rejectObj(error) {
            return rejectGlobal(error)
        }
    })
}

你使用这样的功能......

var process = require('child_process');

function execCommand(req,res,callback){
    var params = req.params.list              //list is an array in the   request
    var result = []

    eachSeries(params, function (param, resolve, reject) {
        cmd = process.exec(param)

        cmd.stdout.on('data', function (data){
            //... after long time you get data, then
            result.push(data.toObject())
            resolve()
        })

        cmd.on("error", function (error){
            reject(error) //If something bad happend
        })
    })
    .then(function () {
        console.log("All process executed one by one and pushed...")
        res.send(result)
    })
    .catch(function (error) {
        console.log("SOmething broke", error)
        res.send(error)
    })
}

或者如果您愿意

var process = require('child_process');

function execCommand(req,res,callback){
    var params = req.params.list              //list is an array in the   request

    eachSeries(params, function (param, resolve, reject) {
        cmd = process.exec(param)

        cmd.stdout.on('data', function (data){
            //... after long time you get data, then
            resolve(data.toObject())
        })

        cmd.on("error", function (error){
            reject(error) //If something bad happend
        })
    })
    .then(function (alldata) {
        res.send(alldata)
    })
    .catch(function (error) {
        res.send(error)
    })
}