【问题标题】:NODEjs synchronizing exec statements inside for loopNODEjs在for循环中同步exec语句
【发布时间】:2016-11-13 00:57:46
【问题描述】:

我想创建一个程序,在其中我从目录中读取文件,然后在其他目录中 grep 这些名称。

代码如下:

var exec = require('child_process').exec;
var internalCount = 0;
var i = -1;
for (; i < fileNamesInDir.length / 2;) {
  if (i === internalCount) {
    continue;
  }
  (function () {
    i = internalCount;
    var j = i;
    exec('grep -nre js/' + fileNamesInDir[j] +
      ' ./ --exclude-dir=node_modules --exclude=searchResults.txt',
      function (error, stdout, stderr) {
        outputString += "\n";
        outputString +=
          "***********************************************************";
        outputString += "\n";
        outputString += "RESULTS FOR  ::" + fileNamesInDir[j];
        outputString += "\n";
        outputString +=
          "***********************************************************";
        outputString += "\n";
        outputString += stdout;
        outputString += "\n";
        if (++internalCount === fileNamesInDir.length) {
          writeToFile();
        }
      });
  })();
}

问题是执行语句不是同步的,回调函数异步返回数据。为了解决这个问题,如果迭代索引与该变量相同,我采用了另一个变量并继续 for 循环。但是,使用此代码,它会进入无限循环,不会触发任何 exec 语句。

【问题讨论】:

  • 你在哪里增加i
  • 是的..一旦回调函数执行..internalcount 就会增加。所以对于下一次迭代 i 不等于 internalCount 然后 continue 不会发生并且自执行函数将运行

标签: javascript node.js exec fs


【解决方案1】:

Javascript 中的for 循环同步运行,这里无法告诉它等待循环中的任何内容。如果您尝试各种 hack,由于 Javascript 的单线程特性,您最终会遇到障碍或无限循环。所以......基本上你不能让for循环等待循环内的异步操作完成,然后再进入循环的下一次迭代。因此,您必须以不同的方式解决您的问题。


这是一种异步迭代的常见方案,其中操作被协调并一个接一个地完成:

var i = 0;
var outputString = "";
function next() {
    if (i < fileNamesInDir.length) {
        exec('grep -nre js/' + fileNamesInDir[i] +
          ' ./ --exclude-dir=node_modules --exclude=searchResults.txt',
          function (error, stdout, stderr) {
            outputString += "\n";
            outputString +=
              "***********************************************************";
            outputString += "\n";
            outputString += "RESULTS FOR  ::" + fileNamesInDir[i];
            outputString += "\n";
            outputString +=
              "***********************************************************";
            outputString += "\n";
            outputString += stdout;
            outputString += "\n";
            i++;
            next();
        });
    } else {
        writeToFile();
    }
}
next();

这将连续运行每个 grep 操作,以便按顺序连接结果。


但是,您不必连续运行此类代码。只要按如下顺序收集结果,就可以并行运行所有这些操作:

var exec = require('child_process').exec;
var results = [];
var doneCntr = 0;
fileNamesInDir.forEach(function(item, index) {
        exec('grep -nre js/' + item +
          ' ./ --exclude-dir=node_modules --exclude=searchResults.txt',
          function (error, stdout, stderr) {
            var outputString = "";
            outputString += "\n";
            outputString +=
              "***********************************************************";
            outputString += "\n";
            outputString += "RESULTS FOR  ::" + item;
            outputString += "\n";
            outputString +=
              "***********************************************************";
            outputString += "\n";
            outputString += stdout;
            outputString += "\n";
            results[index] = outputString;
            ++doneCntr;
            if (doneCntr === fileNamesInDir.length) {
                writeToFile(results.join(""));
            }
        });
    });
}

您的代码也缺少需要添加的错误处理。


我最喜欢的方式是使用 promises,让Promise.all() 为我们完成收集结果的工作:

function execP(cmd) {
    return new Promise(function(resolve, reject) {
        exec(cmd, function(err, stdout, stderr) {
            if (err) {
                reject(err);
            } else {
                resolve({stdout, stderr});
            }
        });
    });
}

Promise.all(fileNamesInDir.map(function(item) {
    return execP('grep -nre js/' + item +
          ' ./ --exclude-dir=node_modules --exclude=searchResults.txt').then(function(data) {
        return "\n" +
           "***********************************************************\n" + 
           "RESULTS FOR  ::" + item + "\n" + 
           "***********************************************************\n" +
           data.stdout + "\n";

    });
})).then(function(results) {
    var output = results.join("");
    // process output here
}, function(err) {
    // process error here
});

【讨论】:

  • 如果execSyncfor 循环中使用,它可以工作。意味着假设for循环的3次迭代,所以它在循环内等待每次迭代的execSync
  • @Yusuf - 是的,如果适合您的程序,您可以使用阻塞 execSync()。任何服务器都不应该使用阻塞 I/O,因为它会破坏服务器的可伸缩性。现在在 2021 年,如果您正在等待 promise,您还可以获得 for 循环来等待 await
猜你喜欢
  • 2019-02-15
  • 1970-01-01
  • 1970-01-01
  • 2016-09-04
  • 1970-01-01
  • 1970-01-01
  • 2014-07-04
  • 1970-01-01
  • 2018-11-13
相关资源
最近更新 更多