关于node.js:node中childprocess的回调执行流程的源码分析

31次阅读

共计 3538 个字符,预计需要花费 9 分钟才能阅读完成。

回调打印后果

正确的代码执行流程
const cp = require('child_process');
const path = require('path');
const child = cp.exec('ls -al|grep node_modules', function(err, stdout,stderr){console.log('start------------------');
  console.log(err, stdout, stderr);
  console.log('end------------------');
})
child.stdout.on('data', (chunk) => {console.log('stdout data =', chunk);
});
child.stderr.on('data', (err) => {console.log('stderr data =', err);
});
child.stdin.on('close', ()=>{console.log('stdin close');
});
child.stdout.on('close', () => {console.log('stdout close');
});
child.stderr.on('close', () => {console.log('stderr close');
})
child.on('exit',(exitCode) => {console.log('exit=', exitCode);
})
child.on('close',() => {console.log('close!');
})
代码执行的打印程序
stdout data = drwxr-xr-x  280 chensi  staff    8960  7 21 17:08 node_modules
exit= 0
stdin close
stderr close
start------------------
null drwxr-xr-x  280 chensi  staff    8960  7 21 17:08 node_modules
 
end------------------
close!
stdout close
谬误的代码执行流程

ls -al|grep node_modules 这个命令批改为谬误的命令去执行,来察看回调的执行后果,ls3333333 -al|grep node_modules

const child = cp.exec('ls3333333 -al|grep node_modules', function(err, stdout,stderr){console.log('start------------------');
  console.log(err, stdout, stderr);
  console.log('end------------------');
})
child.stdout.on('data', (chunk) => {console.log('stdout data =', chunk);
});
child.stderr.on('data', (err) => {console.log('stderr data =', err);
});
child.stdin.on('close', ()=>{console.log('stdin close');
});
child.stdout.on('close', () => {console.log('stdout close');
});
child.stderr.on('close', () => {console.log('stderr close');
})
child.on('exit',(exitCode) => {console.log('exit=', exitCode);
})
child.on('close',() => {console.log('close!');
})
stderr data = /bin/sh: ls3333333: command not found
exit= 1
stdin close
stdout close
start------------------
Error: Command failed: ls3333333 -al|grep node_modules
/bin/sh: ls3333333: command not found

    at ChildProcess.exithandler (node:child_process:398:12)
    at ChildProcess.emit (node:events:539:35)
    at maybeClose (node:internal/child_process:1092:16)
    at Socket.<anonymous> (node:internal/child_process:451:11)
    at Socket.emit (node:events:539:35)
    at Pipe.<anonymous> (node:net:709:12) {
  code: 1,
  killed: false,
  signal: null,
  cmd: 'ls3333333 -al|grep node_modules'
}  /bin/sh: ls3333333: command not found

end------------------
close!
stderr close

从打印后果能够看出,正确的输入后果和谬误的输入后果,在打印上还是存在差别,为此我钻研了源码,为了看上去不太干燥,我将尽可能的用通俗易懂的形式来进行形容,当然如果感觉还是看源码比拟清晰一点,也能够对照 node 源码来看,或者看我上两篇文章;我这里应用的是 node v12 版本;执行原理和目前的 node 版本没有差异;我手绘了一张回调函数的执行流程图;
解答:

  • 为什么打印程序会是这样的后果?
  • 谬误的执行打印程序又为何是这样的?

执行流程

形容
  • Process 执行命令:子过程执行命令是通过 this._hanlde.spawn 办法实现的
  • 执行 C ++ 的代码后,会返回是否存在异样的一个 code 码;返回 0 示意胜利,小于 0 示意失败;
  • 胜利会进到 onStreamRead 办法,这个办法次要是来监听子过程往流填写信息,同时会派发一个 data 事件;child.stdout.on('data',()=>{}), child.stderr.on('data',()=>{})
  • 当流中的信息填写实现后,认为流曾经完结了。会敞开回调办法onReadableStreamEnd,对所有的 socket 进行播送一个 close 事件;只有 socket 能够监听到child.stdout.on(close',()=>{}), child.stderr.on('close',()=>{})
  • 接下来会进行一系列的敞开操作
  • Process 会执行敞开流程,回调子过程中的 onexit 办法,Process.onexit, 派发一个 exit 事件,示意子过程曾经执行实现,过程退出;child.on('exit',() => {})
  • 最初 Socket all close 所有的 Scoket 端口都敞开。派发一个 close 事件,child.on('close',() => {})

因为过程是异步执行的所有两条线

Process 的过程执行流程
  1. 执行实现之后就会执行Process.onexit 敞开过程
流的读取线
  1. Process 过程执行完后,在 pipe 管道中进行信息的写入;onStreamRead,一共三个流,别离是输出流、输入流、谬误流,会循环调用 onStreamRead 函数,并且 emit data 事件;
  2. 所有的信息填写实现之后,调用onReadableStreamEnd,onReadableStreamEnd 会调用 destroy 办法进行敞开流程,将以后的 pipe 和 Socket 进行敞开,socket 敞开当前会调用 emit close 事件,所有的 socket emit colose 事件都会监听到当前,会调用 mybeClose;
  3. 当 mybClose 判断所有的 Socket(Socket all close)所有的流敞开完当前,派发 close 事件;

    流读取谬误:当监听到异样的时候,还会 emit err 事件,因为流没有读取所有间接敞开,onStreamReadableEnd

总结

data/error/close/exit 的区别
  • data:主过程读取数据过程中 通过 onStreamRead 去派发的 data 事件。
  • error:命令执行失败后发动的回调。
  • close:子过程所有的 socket 通信端口全副敞开之后发动的回调。
  • exit:子过程敞开实现后发动的回调。
  • stdout close/ stderr close:特定的 PIPE 读取实现之后执行 onReadableStreamEnd 敞开 Stock 是发动的回调。

正文完
 0