Shell执行期间无法通过socket.io发出当前服务器状态

时间:2019-08-21 23:33:37

标签: javascript node.js angular express socket.io

我想执行一个shell生成脚本,并将当前执行状态发送给angular客户。问题是,调用后不会立即执行发射。程序等待完成全部功能,然后将所有这些发射一起发送。

const express = require('express');
const app = express();
const socketIO = require('socket.io');
const execSync = require('child_process').execSync;

[...]

app.post('/bootupserver', function(req, res) {
  progress = [];
  io.emit('update', getCurrentServerState());

  const filePath = path.join(__dirname, 'bootserver.json');
  const file = fs.readFileSync(filePath, { encoding: 'utf-8' });

  progress = Object.values(JSON.parse(file));
  progress.forEach(line => {
      line[0] = 1;
      io.emit('update', getCurrentServerState());
      execSync(line[2]);
      line[0] = 2;
  });
  io.emit('update', getCurrentServerState());

  res.sendStatus(200);
});

[...]

要了解forEach,这里是我的虚拟bootserver.json。一个数组包含三个值:状态(0 =等待,1 = wip,2 =完成),名称和命令。

{
  "0": [2, "0", "sleep 2"],
  "1": [2, "A", "sleep 1"],
  "2": [1, "b", "sleep 0.2"],
  "3": [0, "C", "sleep 1"],
  "4": [0, "d", "sleep 0.2"],
  "5": [0, "E", "sleep 1"],
  "6": [0, "f", "sleep 5"],
  "7": [0, "G", "sleep 2"],
}

这是我用来收听消息的角度代码:

const observable = new Observable<string>((observer: any) => {
  io(url, { secure: true }).on('update', (msg: string) => {
    observer.next(msg);
  });
});

我希望有一种冲洗/强制发射直接更新前端的机制。有这种吗?

1 个答案:

答案 0 :(得分:1)

您正在使用同步操作,它们阻止了事件循环,因此异步操作(例如网络连接)可能直到事件循环可用时才能继续进行。

您可以通过消除同步操作(用异步操作重写)来解决此问题,以便事件循环可以在循环期间进行操作。

这是一种使用异步I / O进行编码并使用async/await来使循环更容易工作的方法:

const express = require('express');
const app = express();
const socketIO = require('socket.io');
const exec = require('child_process').exec;
const promisify = require('util').promisify;
const fsp = require('fs').promises;

const execP = promisify(exec);

app.post('/bootupserver', async function(req, res) {
  progress = [];
  io.emit('update', getCurrentServerState());

  try {

      const filePath = path.join(__dirname, 'bootserver.json');
      const file = await fsp.readFile(filePath, { encoding: 'utf-8' });

      progress = Object.values(JSON.parse(file));
      for (let line of progress) {
          line[0] = 1;
          io.emit('update', getCurrentServerState());
          await execP(line[2]);
          line[0] = 2;
      }
      io.emit('update', getCurrentServerState());

      res.sendStatus(200);
  } catch(e) {
      res.sendStatus(500);
  }
});
  

我希望有一种冲洗/强制发射直接更新前端的机制。有这种吗?

不直接。您必须将循环的每个迭代放入setTimeout()中(因此不能使用常规的for.forEach()循环),以便让其他东西有运行的机会在运行循环的下一个迭代之前。更好的选择是仅切换到异步设计,以便所有内容可以协同运行。这就是设计node.js的方式。


如果您感到好奇,util.promisify()会特别对待exec(),以便其回调看起来像(err, stdio, stderr)而不是标准的(err, value)异步回调仍能正常工作(它将两个结果内部更改为{stdio, stderr},因此这是一个可以兑现承诺的值)。您没有使用这些返回的值,所以在这里没关系,但是如果您使用的话,它们仍然可以正常工作。