Прежде всего, если это просто одноразовый скрипт, который вы запускаете время от времени, и это не код на сервере, тогда нет необходимости использовать более жесткий асинхронный ввод-вывод.Вы можете использовать синхронный, блокируя вызовы ввода / вывода, такие как fs.openSync()
, fs.statSync()
, fs.readSync()
и т. Д., А затем решите, что будут работать внутри цикла while, потому что эти вызовы блокируются (они не возвращаются дорезультаты сделаны).Вы можете написать нормальный цикл и последовательный код с ними.Никогда не следует использовать синхронный, блокирующий ввод / вывод в серверной среде, потому что это разрушает масштабируемость серверного процесса (его способность обрабатывать запросы от нескольких клиентов), но если это одноразовый локальный сценарий, выполняемый только одной задачей, тогда синхронный ввод / вывод совершенно уместен.
Во-вторых, вот почему ваш код не работает должным образом.Javascript в node.js является однопоточным и управляемым событиями.Это означает, что интерпретатор извлекает событие из очереди событий, запускает код, связанный с этим событием, и ничего не делает, пока этот код не вернет управление обратно интерпретатору.В этот момент он вытаскивает следующее событие из очереди событий и запускает его.
Когда вы делаете это:
while(going) {
fs.read(... => (err, data) {
// some logic here that may change the value of the going variable
});
}
Вы только что создали себе бесконечный цикл.Это потому, что цикл while(going)
просто работает вечно.Он никогда не останавливает цикл и никогда не возвращает управление обратно интерпретатору, чтобы он мог извлечь следующее событие из очереди событий.Это просто продолжает цикл.Но завершение асинхронного неблокирующего fs.read()
происходит через очередь событий.Итак, вы ждете изменения флага going
, но никогда не позволяете системе обрабатывать события, которые могут фактически изменить флаг going
.В вашем реальном случае вы, вероятно, в конечном итоге исчерпаете какой-то ресурс из-за слишком большого количества вызовов fs.read()
в узком цикле, иначе интерпретатор просто зависнет в бесконечном цикле.
Понимание того, как программироватьПовторяющиеся циклические задачи с асинхронными операциями требуют изучения некоторых новых методов программирования.Поскольку большая часть операций ввода-вывода в node.js является асинхронной и неблокирующей, это важный навык, который необходимо развить для программирования node.js.
Существует несколько различных способов решения этой проблемы:
Используйте fs.createReadStream()
и прочитайте файл, прослушивая событие data
.Это, наверное, самая чистая схема.Если ваша цель здесь заключается в создании шестнадцатеричного выходного сигнала, вы можете даже захотеть изучить функцию потока, называемую преобразованием, в которой вы преобразуете двоичный поток в шестнадцатеричный поток.
Используйте обещанные версии всехсоответствующие функции fs
здесь и используют async / await, чтобы позволить вашему циклу for
дождаться завершения асинхронной операции, прежде чем перейти к следующей итерации.Это позволяет вам писать синхронно выглядящий код, но использовать асинхронный ввод / вывод.
Записать другой тип циклической конструкции (без использования while), которая вручную повторяет цикл после fs.read()
завершает.
Вот простой пример использования fs.createReadStream()
:
const fs = require('fs');
function convertToHex(val) {
let str = val.toString(16);
if (str.length < 2) {
str = "0" + str;
}
return str.toUpperCase();
}
let stream = fs.createReadStream(process.argv[2]);
let outputBuffer = "";
stream.on('data', (data) => {
// you get an unknown length chunk of data from the file here in a Buffer object
for (const val of data) {
outputBuffer += convertToHex(val) + " ";
if (outputBuffer.length > 100) {
console.log(outputBuffer);
outputBuffer = "";
}
}
}).on('error', err => {
// some sort of error reading the file
console.log(err);
}).on('end', () => {
// output any remaining buffer
console.log(outputBuffer);
});
Надеюсь, вы заметите это, потому что поток обрабатывает открытие, закрытие и чтениеиз файла для вас, что это гораздо более простой способ кодирования.Все, что вам нужно сделать, это предоставить обработчики событий для данных, которые читаются, ошибки чтения и окончания операции.
Вот версия, использующая async / await и новый файловый интерфейс (где файлдескриптор - это объект, для которого вы вызываете методы) с обещаниями в узле v10.
const fs = require('fs').promises;
function convertToHex(val) {
let str = val.toString(16);
if (str.length < 2) {
str = "0" + str;
}
return str.toUpperCase();
}
async function run() {
const readSize = 8192;
let cntr = 0;
const buffer = Buffer.alloc(readSize);
const fd = await fs.open(process.argv[2], 'r');
try {
let outputBuffer = "";
while (true) {
let data = await fd.read(buffer, 0, readSize, null);
for (let i = 0; i < data.bytesRead; i++) {
cntr++;
outputBuffer += convertToHex(buffer.readUInt8(i)) + " ";
if (outputBuffer.length > 100) {
console.log(outputBuffer);
outputBuffer = "";
}
}
// see if all data has been read
if (data.bytesRead !== readSize) {
console.log(outputBuffer);
break;
}
}
} finally {
await fd.close();
}
return cntr;
}
run().then(cntr => {
console.log(`done - ${cntr} bytes read`);
}).catch(err => {
console.log(err);
});