Потоки в Node.js могут быть сложными, но стоит потратить время на их понимание.
Ключевые выводы
- Потоки в Node.js — это фундаментальный инструмент обработки и передачи данных, что делает их идеальными для приложений реального времени и приложений, управляемых событиями.
- Чтобы создать записываемый поток в Node.js, вы можете использовать функцию createWriteStream() модуля fs, которая записывает данные в определенное место.
- Доступные для чтения, записи, дуплексные и преобразованные — это четыре типа потоков в Node.js, каждый из которых имеет свой вариант использования и функциональность.
Поток — это фундаментальный инструмент программирования, который имеет дело с потоком данных. По своей сути поток обычно представляет собой последовательную передачу байтов из одной точки в другую. Официальная документация Node.js определяет поток как абстрактный интерфейс, который можно использовать для работы с данными.
Передача данных на компьютер или по сети — идеальное использование потока.
Потоки в Node.js
Потоки сыграли важную роль в успехе Node.js. Они идеально подходят для обработки данных в реальном времени и приложений, управляемых событиями, — двух важных особенностей среды выполнения Node.js.
Чтобы создать новый поток в Node.js, вам необходимо использовать API потока, который работает исключительно со строками и Данные буфера Node.js. Node.js имеет четыре типа потоков: записываемые, читаемые, дуплексные и преобразующие.
Как создать и использовать записываемый поток
Доступный для записи поток позволяет записывать или отправлять данные в определенное место. Модуль fs (файловая система) имеет класс WriteStream, который можно использовать для создания нового потока с помощью фс.createWriteStream() функция. Эта функция принимает путь к файлу, в который вы хотите записать данные, а также необязательный массив параметров.
const {createWriteStream} = require("fs");(() => {
const file = "myFile.txt";
const myWriteStream = createWriteStream(file);
let x = 0;
const writeNumber = 10000;
const writeData = () => {
while (x < writeNumber) {
const chunk = Buffer.from(`${x}, `, "utf-8");
if (x writeNumber - 1) return myWriteStream.end(chunk);
if (!myWriteStream.write(chunk)) break;
x++
}
};
writeData();
})();
Этот код импортирует создатьWriteStream() функция, которая анонимная стрелочная функция затем использует для создания потока, который записывает данные в myFile.txt. Анонимная функция содержит внутреннюю функцию, называемую запись данных() который записывает данные.
создатьWriteStream() Функция работает с буфером для записи набора чисел (0–9999) в целевой файл. Однако когда вы запускаете приведенный выше сценарий, он создает в том же каталоге файл, содержащий следующие данные:
Текущая коллекция чисел заканчивается на 2915, но она должна была включать числа до 9999. Это несоответствие возникает потому, что каждый WriteStream использует буфер, в котором одновременно хранится фиксированный объем данных. Чтобы узнать, что такое значение по умолчанию, вам необходимо обратиться к высокий водяной знак вариант.
console.log("The highWaterMark value is: " +
myWriteStream.writableHighWaterMark + " bytes.");
Добавление приведенной выше строки кода в анонимную функцию приведет к следующему выводу в терминале:
Вывод терминала показывает, что по умолчанию высокий водяной знак значение (которое можно настроить) составляет 16 384 байта. Это означает, что вы можете одновременно хранить в этом буфере не более 16 384 байт данных. Таким образом, число до 2915 (плюс все запятые и пробелы) представляет собой максимальный объем данных, которые буфер может хранить одновременно.
Решением ошибки буфера является использование события потока. Поток сталкивается с различными событиями на разных этапах процесса передачи данных. осушать event — подходящий вариант для этой ситуации.
в запись данных() вышеописанную функцию, вызов WriteStream’s write() функция возвращает true, если фрагмент данных (или внутренний буфер) находится ниже высокий водяной знак ценить. Это указывает на то, что приложение может отправить в поток больше данных. Однако, как только писать() Функция возвращает false, цикл прерывается, поскольку вам нужно очистить буфер.
myWriteStream.on('drain', () => {
console.log("a drain has occurred...");
writeData();
});
Вставка осушать приведенный выше код события в анонимную функцию очистит Буфер WriteStream когда он на полную мощность. Затем он напоминает запись данных() метод, чтобы он мог продолжить запись данных. Запуск обновленного приложения приведет к следующему выводу:
Следует отметить, что приложению пришлось слить Буфер WriteStream три раза за время его исполнения. Текстовый файл также претерпел некоторые изменения:
Как создать и использовать читаемый поток
Чтобы прочитать данные, начните с создания читаемого потока с помощью фс.createReadStream() функция.
const {createReadStream} = require("fs");
(() => {
const file = "myFile.txt";
const myReadStream = createReadStream(file);myReadStream.on("open", () => {
console.log(`The read stream has successfully opened ${file}.`);
});myReadStream.on("data", chunk => {
console.log("The file contains the following data: " + chunk.toString());
});
myReadStream.on("close", () => {
console.log("The file has been successfully closed.");
});
})();
В приведенном выше сценарии используется создатьReadStream() метод для доступа к файлу, созданному предыдущим кодом: myFile.txt. создатьReadStream() функция принимает путь к файлу (который может быть в форме строки, буфера или URL-адреса) и несколько дополнительных параметров в качестве аргументов.
В анонимной функции есть несколько важных событий потока. Однако нет никаких признаков осушать событие. Это связано с тем, что читаемый поток буферизует данные только тогда, когда вы вызываете поток.push (кусок) функцию или используйте удобочитаемый событие.
открыть Событие срабатывает, когда fs открывает файл, из которого вы хотите прочитать. Когда вы прикрепите данные событие в неявно непрерывный поток, оно приводит к переходу потока в потоковый режим. Это позволяет передавать данные, как только они становятся доступными. Запуск приложения выше дает следующий результат:
Как создать и использовать дуплексный поток
Дуплексный поток реализует как записываемые, так и читаемые потоковые интерфейсы, поэтому вы можете читать и записывать в такой поток. Одним из примеров является сокет TCP, при создании которого используется модуль net.
Простой способ продемонстрировать свойства дуплексного потока — создать TCP-сервер и клиент, передающий данные.
Файл server.js
const net = require('net');
const port = 5000;
const host = '127.0.0.1';const server = net.createServer();
server.on('connection', (socket)=> {
console.log('Connection established from client.');socket.on('data', (data) => {
console.log(data.toString());
});socket.write("Hi client, I am server " + server.address().address);
socket.on('close', ()=> {
console.log('the socket is closed')
});
});
server.listen(port, host, () => {
console.log('TCP server is running on port: ' + port);
});
Файл client.js
const net = require('net');
const client = new net.Socket();
const port = 5000;
const host = '127.0.0.1';client.connect(port, host, ()=> {
console.log("connected to server!");
client.write("Hi, I'm client " + client.address().address);
});client.on('data', (data) => {
console.log(data.toString());
client.write("Goodbye");
client.end();
});
client.on('end', () => {
console.log('disconnected from server.');
});
Вы заметите, что и серверный, и клиентский сценарии используют поток, доступный для чтения и записи, для связи (передачи и получения данных). Естественно, серверное приложение запускается первым и начинает прослушивать соединения. Как только вы запускаете клиент, он подключается к серверу с помощью номер TCP-порта.
После установления соединения клиент инициирует передачу данных, записывая их на сервер, используя свой WriteStream. Сервер регистрирует полученные данные на терминале, а затем записывает данные, используя свой WriteStream. Наконец, клиент регистрирует полученные данные, записывает дополнительные данные, а затем отключается от сервера. Сервер остается открытым для подключения других клиентов.
Как создать и использовать поток преобразования
Потоки преобразования — это дуплексные потоки, в которых выходные данные связаны с входными данными, но отличаются от них. Node.js имеет два типа потоков преобразования: потоки zlib и криптопотоки. Поток zlib может сжимать текстовый файл, а затем распаковывать его после передачи файла.
Приложение compressFile.js
const zlib = require('zlib');
const { createReadStream, createWriteStream } = require('fs');(() => {
const source = createReadStream('myFile.txt');
const destination = createWriteStream('myFile.txt.gz');
source.pipe(zlib.createGzip()).pipe(destination);
})();
Этот простой сценарий берет исходный текстовый файл, сжимает его и сохраняет в текущем каталоге. Это простой процесс благодаря читаемому потоку трубка() метод. Потоковые конвейеры исключают использование буферов и передают данные непосредственно из одного потока в другой.
Однако, прежде чем данные достигнут записываемого потока в скрипте, требуется небольшой обход с помощью метода createGzip() библиотеки zlib. Этот метод сжимает файл и возвращает новый объект Gzip, который затем получает поток записи.
Приложение decompressFile.js
const zlib = require('zlib');
const { createReadStream, createWriteStream } = require('fs');
(() => {
const source = createReadStream('myFile.txt.gz');
const destination = createWriteStream('myFile2.txt');
source.pipe(zlib.createUnzip()).pipe(destination);
})();
Этот сценарий выше берет сжатый файл и распаковывает его. Если вы откроете новый мойФайл2.txt файл, вы увидите, что он содержит те же данные, что и исходный файл:
Почему потоки важны?
Потоки повышают эффективность передачи данных. Доступные для чтения и записи потоки служат основой, обеспечивающей связь между клиентами и серверами, а также сжатие и передачу больших файлов.
Потоки также улучшают производительность языков программирования. Без потоков процесс передачи данных становится более сложным, требующим большего ручного ввода от разработчиков и приводящим к большему количеству ошибок и проблем с производительностью.