Используйте spawn() для создания дочернего процесса, но все равно блокирует текущую вызывающую функцию

#node.js #subprocess #ipc

Вопрос:

У меня есть функция, которая асинхронно порождает дочерний процесс, чтобы я мог прослушивать ошибки дочернего процесса, например:

 
function runCmd(cmd, args, cwd) {
        let childProc = childProcess.spawn(cmd, args, {
            encoding: 'utf8',
            cwd: cwd,
            stdio: ['ignore', 'pipe', 'pipe']
        });
        childProc.on('error', (err) => {
            console.error(`backend error: ${err}`);
        })
        childProc.stdout.on('data', (data) => {
            console.log(`stdout: ${data}`);
        });
          
        childProc.stderr.on('data', (data) => {
            console.error(`stderr: ${data}`);
        });
          
        childProc.on('close', (code) => {
            console.log(`child process closed with code ${code}`);
        });

        childProc.on('exit', () => {
            console.log(`child process is gone`);
        });
}
 

Наблюдение против Ожидание

Однако эта функция немедленно завершается, в то время как я хочу, чтобы она блокировалась до тех пор, пока я не найду способ выйти в одном из прослушивателей событий.

Я пробовал различные способы, такие как использование цикла while(true) (он блокирует все, включая дочерний процесс, потому что NodeJS однопоточный), или обещание, такое как

 await new Promise( (resolve) => {
    child.on('close', resolve)
})

 

Обходные пути

Я мог бы использовать spawnSync , но он зависает, если происходит сбой дочернего процесса, без каких-либо ошибок на этом пути.

Самое забавное, что если я помещу spawnSync его в блок try-catch, я все равно ничего не получу в блоке catch, что означает spawnSync() , что вызов просто зависает.

Мне было удобно работать с модулем подпроцесса Python, где родительский процесс всегда получает информацию обратно при сбое синхронно созданного дочернего процесса.

Поэтому способ Нодейса справиться с этим, то есть просто зависнуть навсегда, мне кажется немного странным.

Вопросы

Как я мог бы получить лучшее из обоих миров spawn и spawnSync ? т. Е.,

  • Заблокируйте функцию до тех пор, пока дочерний процесс не завершится или не завершится сбоем
  • Получите всю информацию о stdout/stderr из дочернего процесса, несмотря ни на что

Комментарии:

1. Как вы сказали, spawn является асинхронной, функция runCmd возвращается, не дожидаясь завершения вашего дочернего процесса, поэтому она всегда завершается немедленно. И если вы используете spawnSync , вам нужно поместить spawnSync в try-catch блок, тогда вы можете поймать ошибки.

2. @lx1412 Самое смешное, что он на самом деле никогда не доходит до моего блока catch. Он просто зависает на spawnSync.

3. Это не сработает так, как вы хотите, и попытка втиснуть это в вашу уже существующую ментальную модель не закончится хорошо. Блокировка основного потока дочернего процесса обычно не является хорошей идеей ни на одном языке. Самое близкое, к чему вы можете подойти, — это чтобы ваша функция возвращала обещание, которое разрешается в обработчике выхода вашего ребенка. Вы можете await это сделать в вызывающем абоненте.