Многие люди, похоже, не понимают, как однопоточный NodeJS может конкурировать с многопоточными бэкэндами.
Чтобы выяснить, почему, нам нужно понять, что на самом деле означает, что Nodejs является однопоточным.
Сам JavaScript изначально был создан для выполнения простых задач, таких как проверка форм, обработка ответов и т. д. Лишь в 2009 году создатель Node.js Райан Даль сделал возможным писать серверный код с использованием JavaScript.
Серверные языки, поддерживающие многопоточность, имеют различные структуры и конструкции для синхронизации между потоками и другими потоко-ориентированными функциями.
Поддержка этих вещей будет означать, что JavaScript придется изменить весь язык, что противоречит идеям создателей JavaScript. Поэтому, чтобы поддерживать многопоточность в чистом JavaScript, Далю пришлось найти обходной путь. Давайте посмотрим!
Как работает Node.js?
Node.js использует два типа потоков: основной поток, который обрабатывается циклом событий, и несколько второстепенных потоков в пуле рабочих потоков.
Механизм Event Loop Node.js для обработки неблокирующих операций ввода-вывода — даже несмотря на то, что JavaScript является однопоточным — по возможности перекладывает операции на ядро системы. Когда операция JavaScript блокирует поток, цикл событий также блокируется.
Рабочий пул — это модель выполнения, которая порождает и обрабатывает отдельные потоки, затем синхронно выполняет задачи и возвращает результаты в цикл обработки событий. Затем цикл событий использует указанный результат для выполнения предоставленного обратного вызова.
По сути, рабочий пул обрабатывает асинхронные операции ввода-вывода — в первую очередь взаимодействие с системным диском и сетью. Некоторые модули по умолчанию используют рабочие пулы, такие как fs (интенсивный ввод-вывод) или криптографический (нагруженный процессор). Пул рабочих реализован в libuv, что вызывает небольшую, но почти незначительную задержку, когда Node требуется внутренняя передача данных между JavaScript и C++.
Поняв значение цикла событий и рабочего пула, давайте посмотрим на следующий код:
В приведенном выше коде нам не нужно ждать события синхронно. Мы делегируем задачу чтения файла рабочему пулу и вызываем предоставленную функцию с результатом. Поскольку рабочий пул имеет собственный поток, цикл обработки событий может продолжать нормально выполняться во время чтения файла.
Позвольте представить вам: worker_threads
С выходом Node.js 10.5.0 появился worker_threads. Он поддерживает создание простых многопоточных приложений в JavaScript.
worker_threads — это пакет модулей nodejs. Поток-воркер — это фрагмент кода (обычно взятый из файла), сгенерированный в отдельном потоке.
Важно отметить, что термины «работник потока», «работник» и «поток» часто используются как взаимозаменяемые. Все они относятся к одному и тому же.
Рабочие потоки в Node.js полезны для выполнения тяжелых задач JavaScript. С помощью потоков Workers могут легко запускать код JavaScript параллельно, делая его быстрее и эффективнее. Мы можем выполнять тяжелые задачи, не нарушая основной поток.
Рабочие потоки не были представлены в старых версиях Node.js. Поэтому сначала обновите Node.js, чтобы начать работу.
Теперь создайте два файла для реализации потоков следующим образом:
Имя файла: worker.js
const { workerData, родительский порт } = require('worker_threads'); console.log(`Запись о том, как ${workerData} хочет пообщаться с большими мальчиками`); ParentPort.postMessage({имя файла: workerData, статус: 'Готово' });
Имя файла: index.js
const {Worker } = require('worker_threads'); const runSerice = (workerData) => { вернуть новое обещание((разрешить, отклонить) => { const работник = новый работник('./worker.js', {workerData}); worker.on('сообщение', разрешить); worker.on('ошибка', отклонить); worker.on('exit', (код) => { если (код !== 0) ignore(new Error(`Рабочий поток остановлен с кодом выхода ${code}`)); }); }); }; const run = async () => { const result = await runSerice('Tunde Ednut'); console.log(результат); }; run().catch((err) => console.error(err))
;