Почему мой Node.js процесс не завершился, как только все слушатели были удалены?
В следующем коде я назначаю слушателю событие data
process.stdin
с помощью метода once
.
console.log('Press Enter to allow process to terminate')
process.stdin.once('data', callback)
function callback (data) {
console.log('Process can terminate now')
}
Теоретически, когда обратный вызов был запущен, прослушиватель событий должен быть автоматически удален (потому что я привязал его к once
), что позволило завершить процесс. Удивительно, но в этом случае процесс никогда не заканчивается (код, который вы видите, это все, попробуйте!). Я также попытался вручную удалить слушателя, но это ничего не меняет.
Есть ли что-то еще, что я не понимаю, возможно?
Ответы
Ответ 1
Добавление прослушивателя событий data
в process.stdin
добавляет ссылку на него, которая удерживает процесс открытым. Эта ссылка остается на месте даже после удаления всех прослушивателей событий. Вы можете сделать это вручную unref()
в своем обратном вызове, например:
console.log('Press Enter to allow process to terminate')
process.stdin.once('data', callback)
function callback (data) {
console.log('Process can terminate now')
process.stdin.unref()
}
Кроме того, в качестве общего инструмента для отладки для таких вещей есть две (недокументированные) функции, которые вы можете вызвать, чтобы получить список вещей, поддерживающих ваш процесс:
process._getActiveHandles()
process._getActiveRequests()
Смотрите этот запрос на растяжение в проекте node для фона.
Обновление:. Вы спросили о подключении слушателей событий после unref()
'd process.stdin
. Вот краткий пример, показывающий, что слушатель сам присоединяется и работает:
console.log('Press Enter to allow process to terminate')
process.stdin.once('data', callback)
function callback (data) {
console.log('Unreferencing stdin. Exiting in 5 seconds.')
process.stdin.unref()
process.stdin.once('data', function(data) {
console.log('More data')
})
setTimeout(function() {
console.log('Timeout, Exiting.')
}, 5000);
}
С помощью этого кода, если вы нажмете еще одну клавишу перед тем, как срабатывает setTimeout
(5 секунд), вы увидите вывод More data
на консоль. После срабатывания обратного вызова setTimeout
процесс завершится. Фокус в том, что setTimeout
создает таймер, который также сохраняет ссылку. Поскольку процесс все еще имеет ссылку на что-то, он не будет сразу уходить. Как только таймер срабатывает, эта ссылка высвобождается, и процесс завершается. Это также показывает, что ссылки добавляются (и удаляются) к вещам, которые им нужны автоматически (таймер, созданный setTimeout
в этом случае).
Ответ 2
Просто вызовите .end
в потоке process.stdin
Для меня это более простой (и documented) способ завершения потока.
console.log('Press Enter to allow process to terminate');
process.stdin.once('data', callback);
function callback (data) {
console.log('Process can terminate now');
process.stdin.end();
}
Также стоит отметить, что node устанавливает поток как контекст для функции обратного вызова, поэтому вы можете просто вызвать this.end
console.log('Press Enter to allow process to terminate');
process.stdin.once('data', callback);
function callback (data) {
// `this` refers to process.stdin here
console.log('Process can terminate now');
this.end();
}
Вы также можете испустить событие end
, которое имеет дополнительные преимущества, такие как возможность вызова функции, когда поток завершен.
console.log('Press Enter to allow process to terminate');
process.stdin.once('data', function(data) {
console.log('Process can terminate now');
this.emit("end");
});
process.stdin.on('end', function() {
console.log("all done now");
});
Это приведет к выводу
Press Enter to allow process to terminate
Process can terminate now
all done now
Конечным решением было бы использовать process.exit
. Это позволяет вам прекратить выполнение программы, когда захотите.
for (var i=0; i<10; i++) {
process.stdout.write( i.toString() );
if (i > 3) process.exit();
}
Выход
01234
Это будет работать внутри обратного вызова потока, как часть дочернего процесса или любого другого кода.