事件已触发,但并非所有代码都已执行

时间:2019-03-19 19:12:25

标签: javascript jquery node.js express

我正在github上名为lan-info的项目上工作。
我有以下代码

let arpSweepCommand = './arpSweep.sh';
app.get('/arp', (req, res) => {
  console.log('we have a working signal!');
  executeCommand(arpSweepCommand, req, res, false);
  fs.readFile('command_output/arpedHosts.txt', 'utf-8', (error, data) => {
    if (error) {
      res.send(error);
    } else {
      res.send(data);
    }
  })
})

和arpSweep.sh包含:

timeout 10s netdiscover -P > command_output/arpedHosts.txt

在前端,我有一个Jquery AJAX调用:

arpButton.click(function () {
  loader.show();
  $.ajax({
    method: 'GET',
    url: '/arp',
    success: function (data) {
      console.log(data);
      commandOutput.append(data);
      loader.hide();
    }
  })
})

我知道没有语法错误,因为webpack会编译前端代码而不会产生任何抱怨;而且我知道后端确实会捕获请求,因为每当我单击arpButton时,它都会显示“我们有信号!”在服务器上。
但是问题是loader.show()/ hide()似乎仅在此ajax请求中什么也不做,我知道这是特定于此ajax请求的,因为我有类似的请求,它们的功能完美。
对我来说,问题是该信息不会自动附加到commandOutput
我需要再次单击arpButton才能将输出附加到commandOutput
但是加载程序不会为我显示,我知道加载程序不会很快出现或消失,因为ajax调用至少需要10秒钟才能完成。
另一个问题是在commandOutput中10秒钟后数据不会自动显示,我通过单击arpButton等待30分钟来测试只有加载器发生故障,但是什么也没发生;当我再次单击arpButton时,输出立即显示。
那为什么页面不更新呢?相反,它迫使我重新单击该按钮。 如果您需要更多信息,可以单击文件上方的项目链接:
src / index.js
index.js
arpSweep.sh
注意:如果不是您使用的子网,请确保在index.js顶部更改子网(自动子网检测是我计划稍后实现的功能)。
编辑:尝试读取文件作为executeCommand的回调后:

function executeCommand (command, req, res, sendRes = true, callback) {
  exec(command, (error, stdout, stderr) => {
    console.log(`stdout: ${stdout}`)
    console.log(`stderr: ${stderr}`)
    if (error !== null) {
      console.log(`Error ${error}`)
    } else if (sendRes === false) {
    } else if (typeof callback === 'function') {
      callback()
    } else {
      res.send(stdout + stderr)
    }
  })
}
app.get('/arp', (req, res) => {
  console.log('we have a working signal!')
  executeCommand(arpSweepCommand, req, res, false, function () {
    fs.readFile('command_output/arpedHosts.txt', 'utf-8', (error, data) => {
      if (error !== null) {
        res.send(error)
      } else {
        res.send(data)
      }
    })
  })
})

我遇到了一个新问题:
现在单击arpButton时,将显示加载程序,但10秒钟后(杀死netdiscover之前分配给arpSweep.sh中的命令的时间)似乎没有什么区别,并且加载器现在似乎永远呆在那里。
在服务器上,我正在捕获以下错误。

error: Error can't execute command './arpSweep.sh'

2 个答案:

答案 0 :(得分:0)

  

对我来说,问题在于信息不会自动附加到commandOutput

您遇到的问题是因为浏览器不知道如何append json(我认为它并不总是json响应)。 尝试附加responseText而不是data

success: function (data, status, xhr) {
  console.log(data)
  commandOutput.append(xhr.responseText + '\r\n')
}

从加载程序开始-一切正常,也许您只需要在success回调中而不是将其隐藏即可。

在这一部分:

complete

您以异步方式调用executeCommand,因此不能确定输出是否来自当前调用(当您需要单击两次时,这可能是问题的核心)。因此,您应该将executeCommand(arpSweepCommand, req, res, false) fs.readFile('command_output/arpedHosts.txt', 'utf-8', (error, data) => { 的代码部分reading file移到exec方法的executeCommand回调内部,如下所示:

function executeCommand (command, req, res, cb) {
  exec(command, (error, stdout, stderr) => {
    console.log(`stdout: ${stdout}`)
    console.log(`stderr: ${stderr}`)
    if (error !== null) {
      console.log(`Error ${error}`)
    } else {
      if (typeof cb === 'function') {
        cb()
      } else {
        res.send(stdout + stderr)
      }
    }
  }
}

还有

executeCommand(arpSweepCommand, req, res, () => {
  fs.readFile('command_output/arpedHosts.txt', 'utf-8', (error, data) => {
    if (error) {
      res.send(error)
    } else {
      res.send(data)
    }
  })
})

答案 1 :(得分:0)

问题在于executeCommand是异步的(它使用child_process.exec),因此您在写入文件之前正在读取文件内容,这就是为什么您需要再次单击按钮以查看内容的原因在下一个请求中。请参见此处的示例以及如何使其与promisifyasync/await保持同步:https://nodejs.org/api/child_process.html#child_process_child_process_exec_command_options_callback

类似这样的东西(未经测试):

const exec = util.promisify(require('child_process').exec);

async function executeCommand (command, req, res, sendRes = true) {
  return exec(command, (error, stdout, stderr) => {
    console.log(`stdout: ${stdout}`)
    console.log(`stderr: ${stderr}`)
    if (error !== null) {
      console.log(`Error ${error}`)
    } else if (sendRes === false) {
    } else {
      res.send(stdout + stderr)
    }
  })
}

然后在您的处理程序中:

await executeCommand(arpSweepCommand, req, res, false);