【问题标题】:Event triggerred but not all the code is executed事件触发但并非所有代码都被执行
【发布时间】:2019-03-20 18:47:12
【问题描述】:

我正在 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'

【问题讨论】:

  • 对不起,我真的不明白你的问题,需要更多信息来帮助,例如与“自动附加”相关的“这不是 ajax 的目的”是什么意思?当服务器上的执行完成并返回数据时,将调用您的 success 方法,因此这正是您在倒数第二句中所说的似乎发现的错误。单击两次按钮似乎可以解决问题,但实际上可能是这样的:click button > script starts and takes 10 seconds > click again > script triggered by first click finishes and returns.
  • 在本地尝试了你的项目,我能说什么 - 加载器一切正常,当你发送带有空字符串的 ping 时 - 加载器显示而不是隐藏,因为它永远不会成功(500 错误)当您执行arp 时 - 加载程序几乎立即显示和隐藏,因为它在大约 5 毫秒内获得 200 OK。因此,如果您希望加载器在服务器上的某些工作正在进行时持续存在 - 您不应该立即返回响应。
  • @SaschaM78 但如果是这种情况,为什么加载程序不显示表明服务器端代码正在执行中

标签: javascript jquery node.js express


【解决方案1】:

我的问题是信息不会自动附加到commandOutput

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

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

就装载机而言 - 一切正常,也许您只需要隐藏它,而不是 success,而是 complete 回调。

在这部分:

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

您异步调用 executeCommand,因此您无法确定输出是否来自当前调用(当您需要单击两次时,这可能是问题的核心)。因此,您应该将 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)
    }
  })
})

【讨论】:

  • 但是 res.send() 不是总是返回文本并且没有将很多东西放在一个函数中违反让一个函数只做一件事的想法吗?
  • > always return text - 实际上,图像响应将无法正常工作。对于任何基于文本的它都可以使用(json 和 xml 也是如此)。 > doesn't putting a lot of things in one function violate the idea of having a function only do one 'thing'?该函数仍然做一件事,然后调用作为参数传递的回调。这里的每个函数都做一件事。
  • 所以callback != function_responsability?
  • @TheLinuxPro 正确。这是链接方法,某些函数不会执行回调中包含的所有代码,它只是在执行结束时调用回调(或者应该调用它的时候)
  • 我在哪里可以了解更多关于这个我从未真正理解回调的概念
【解决方案2】:

问题是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);

【讨论】:

  • 不会像 promisify 这样的额外库会增加膨胀吗?并且没有 async/await 应该解决与回调地狱和承诺复杂相关的问题吗?
  • 我正试图摆脱 jquery,你告诉我'get promisify' 哈哈
  • promisify 来自 node js 中的标准 util 库,您不需要任何额外的库,并且 exec 确实使用回调,这就是为什么我建议使用 promises 和 aync 来扁平化您的代码/await,恕我直言,更具可读性...是的,请在您的前端摆脱 jquery... :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-04
  • 2016-11-29
  • 1970-01-01
  • 1970-01-01
  • 2019-04-30
  • 1970-01-01
相关资源
最近更新 更多