【问题标题】:Promises confusion in NodeJS在 NodeJS 中承诺混淆
【发布时间】:2019-02-24 23:12:42
【问题描述】:

我在思考 NodeJS 上代码执行的异步特性时遇到了很多麻烦。我有一个简单的函数可以在 Linux 机器上获取 ip a 的输出并手动解析 IP 子网。完成后,我只想console.log() IP 子网。

我知道 NodeJS 主要是异步运行的,所以我不能指望在我 console.log() 变量之前完成逻辑。我了解回调的概念来解决这个问题,但我更愿意访问逻辑循环之外的变量。我求助于 Promises,这似乎是一个很好的解决方案,但我认为我遗漏了一些东西,而且它们没有按我预期的方式工作。下面是我的代码:

let subnetString = '';

function getIPLinux() {
  return new Promise((resolve) => {
    const ipOutput = spawn( 'ip', ['a'] );

    ipOutput.stdout.on('data', (data) => {
        String(data).split('\n').forEach( (line) => {

            if ( line.includes('inet') && line.indexOf('inet6') < 0 && line.indexOf('127.0.0.1') < 0 ) {
                const ipSubnet = line.split(' ')[5];
                const ipStringArray = ipSubnet.split('.');
                subnetString = ipStringArray[0] + '.' + ipStringArray[1] + '.' + ipStringArray[2] + '.*';
                console.log('Found subnet at end of if loop: ' + subnetString);
            }

        })
    })

    console.log('Found subnet at end of promise: ' + subnetString);
    resolve();
  })
}

getIPLinux().then( () => {
  console.log('Found subnet after then: ' + subnetString);
});

我的输出如下:

Found subnet at end of promise: 
Found subnet after then: 
Found subnet at end of if loop: 192.168.1.*

只有记录的最后一行是正确的。我无法将注意力集中在这种非阻塞代码执行上。如果我的方法不对,我也愿意接受其他方法。

【问题讨论】:

  • 你能有多个subnetStrings吗?
  • @molamk 不,它只是一个字符串。
  • 查看thisthat 了解如何承诺数据流事件发射器

标签: node.js javascript


【解决方案1】:

spawn() 也是异步的。您正在为 stdout 使用事件回调,这很好,但您正在立即解决低于该承诺的承诺,而无需等待输入完成。试试

ipOutput.on('close', () => {
  console.log('Found subnet at end of promise: ' + subnetString);
  resolve(subnetString);
});

在你的承诺结束时。

https://nodejs.org/api/child_process.html#child_process_event_close

【讨论】:

  • 成功了!关于执行顺序,这也让我有点不高兴了。我最终进一步简化了它,只是把resolve();放在console.log('Found subnet at end of if loop: ' + subnetString);之后
  • 这似乎很好,不太可能成为问题。请记住,虽然不能保证 data 事件只会发生一次并且它会包含您想要的内容。对于大输出,可以多次调用它。即使您想要的是在 data 的第一次调用中,它也可以在您的承诺解决后再次运行,并且如果您正在编辑承诺之外的变量,就像您的问题一样,它可能会改变不可预知的方式。您spawn() 的程序仍在运行并可能产生输出,直到您收到 close 事件。
  • 你说得对。我切换回使用您最初建议的 ipOutput.on('close', ...) 。
【解决方案2】:
  • 你需要resolve你的Promise内部.on('data', (data) =&gt; { // here })回调
  • 而不是subnetString 写为 全局 变量,将其传递到 Promise。喜欢这个resolve(subnetString)
  • 如果有任何错误,请不要忘记catch。例如,如果没有找到有效的行
  • 由于您要返回只有一个 subnetString,您可以使用Array.find 而不是执行forEach 循环。它需要一个 "validation" 回调并返回项目或 null 如果没有找到

const isValidLine = l => l.includes('inet') && !l.includes('inet6') && !l.includes('127.0.0.1');

const extractSubnet = l => {
  const ipStringArray = l.split(' ')[5].split('.');
  return ipStringArray[0] + '.' + ipStringArray[1] + '.' + ipStringArray[2] + '.*';
}

function getIPLinux() {
  return new Promise((resolve, reject) => {
    const ipOutput = spawn('ip', ['a']);

    ipOutput.stdout.on('data', data => {
      const line = data.split('\n').find(isValidLine);

      // Resolve or Reject your promise here
      if (!line) {
        reject('Line not found');
      } else {
        const subnetString = extractSubnet(line);
        resolve(subnetString);
      }
    });
  });
}

getIPLinux().then(subnetString => {
  console.log('Found subnet after then: ' + subnetString);
});

【讨论】:

    【解决方案3】:

    这是一个类似于this one 的问题,没有通过承诺正确解决。

    resolve 在 promise 构造中被同步调用,promise 在数据到达之前解决。一个承诺应该用数据来解决,但它没有。

    考虑到只处理单个data,应该是:

    function getIPLinux() {
      return new Promise((resolve) => {
        const ipOutput = spawn( 'ip', ['a'] );
    
       const handler = (data) => {
            String(data).split('\n').forEach((line) => {
                if (line.includes('inet') && line.indexOf('inet6') < 0 && line.indexOf('127.0.0.1') < 0) {
                    const ipSubnet = line.split(' ')[5];
                    const ipStringArray = ipSubnet.split('.');
                    const subnetString = ipStringArray[0] + '.' + ipStringArray[1] + '.' + ipStringArray[2] + '.*';
                    ipOutput.stdout.off('data', handler);
                    resolve(subnetString);
                }
            })
        })
    
        ipOutput.stdout.on('data', handler);
      })
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-01-19
      • 1970-01-01
      • 1970-01-01
      • 2016-02-22
      • 1970-01-01
      • 2019-09-29
      • 2019-03-19
      相关资源
      最近更新 更多