После долгих копаний я понял, как работает node.js внутри. Этот блог основан на моих исследованиях, наберитесь терпения, чтобы погрузиться глубже!😁

Хорошо! Итак, давайте сначала разберемся, что такое Node.js 🐘. В документации говорится: Node.js® – это среда выполнения JavaScript, созданная на «движке Chrome V8 JavaScript.

Здесь много красивых слов 🤯. Давайте вместе разберемся 💪 Во-первых, что такое среда выполнения и почему node.js является средой выполнения ???

Среда выполнения — это любой код, который выполняется для того, чтобы ваш код работал. Код, в котором мы пишем какой-то код, который будет выполняться средой выполнения. Среда выполнения может состоять из компилятора, интерпретатора, механизма для выполнения вашего кода, пула потоков для предоставления потоков для процесса вашего кода.

Движок — это фактический код, отвечающий за выполнение вашей программы. Он преобразует ваш код в машинный код, чтобы ваш компьютер (ЦП) мог его выполнить.

В JavaScript — механизм JavaScript преобразует ваш сценарий в исполняемые инструкции машинного кода, чтобы он мог выполняться процессором.

Среда выполнения JavaScript похожа на большой контейнер, который содержит другие небольшие контейнеры, такие как механизм, куча, стек, веб-API, цикл событий, очередь обратных вызовов. Когда движок JS анализирует код, он начинает помещать его части в разные контейнеры.

Важно то, что реализация механизма JavaScript полностью независима от среды выполнения. Механизмы не разрабатываются с учетом какой-либо конкретной среды.

Например, браузер Chrome и node.js используют один и тот же движок — V8, но их среда выполнения отличается: в Chrome у вас есть окно, объекты DOM и т. д., а узел предоставляет вам буферы и процессы.

Представьте, что робот готовит еду:

  • Ваш код будет инструкциями для робота по приготовлению еды.
  • Двигатель будет роботом, который может понимать инструкции и действовать в соответствии с ними.
  • Время выполнения будет газовая плита LPG и посуда.

Node.js — это среда выполнения, поскольку она имеет движок V8, libuv threadpool и OS Async Helpers. Все это дает вам возможность писать код JavaScript на сервере.

Переходя к другому модному слову: «двигатель V8»🦋

В документации говорится: «V8 — это высокопроизводительный механизм JavaScript и WebAssembly с открытым исходным кодом, написанный Google на C++».

Ну, это означает, что V8 — это программа на C++, которая получает код JavaScript, компилирует и выполняет его. Обязанности V8:

  1. Компилирует и выполняет JS-код.
  2. Обработка стека вызовов для запуска ваших функций JS в некотором порядке
  3. Управление выделением памяти для объектов — куча памяти
  4. Сборка мусора объектов, которые больше не используются
  5. Предоставьте все типы данных, операторы, объекты и функции

Примечание. V8 ничего не знает об объектной модели документа (DOM), которая предоставляется браузером.

Давайте разберемся с потоками и процессами, прежде чем разбираться в цикле событий.

Все, что делает ваш компьютер, выполняется с помощью процесса. Например, файл открывается процессом текстового редактора. Музыка воспроизводится процессом музыкального проигрывателя.

Точно так же для запуска вашей программы node требуется процесс. Этот процесс создается, как только вы запускаете node в своем терминале.

У каждого процесса есть потоки, которые фактически выполняют код внутри программы процесса. Потоки используют ядра ЦП для выполнения кода. Процесс может иметь более одного потока.

Итак, когда вы запускаете следующий код с использованием node: const mul= 4*2; — создается процесс, и ему назначаются потоки для выполнения кода.

Процесс NodeJS не является «однопоточным». «Цикл событий» является однопоточным

Цикл событий

Рассмотрим следующий код:

console.log("This is the first statement");
setTimeout(function(){
console.log("This is the second statement");
}, 5000);
console.log("This is the third statement");

Вывод

This is the first statement
This is the third statement
This is the second statement

Node.js не ждет 5 секунд для второго оператора, а выполняет третий оператор и параллельно ожидает истечения 5-секундного таймера, несмотря на то, что он однопоточный. Как? Цикл событий спешит на помощь!

«Цикл событий» — это просто цикл, подобный циклу for или циклу while. Когда вы запускаете процесс своего узла, запуская node app.js, выполняются следующие шаги:

  1. Движок V8 выполняет ваш код в app.js
  2. Двигатель V8 немедленно запускает «Цикл событий».

Итак, теперь должно быть совершенно ясно одно: цикл обработки событий не выполняет ваш код, за выполнение вашего кода отвечает движок V8.

Цикл событий — это фрагмент кода в цикле, который берет задачи из Очереди заданий/задач и отправляет их в стек вызовов для выполнения.

Ждать! Подожди! Какие задачи и что такое «Очередь заданий»?

Таким образом, очередь заданий представляет собой структуру данных FIFO. Задачи, отправленные первыми, первыми будут выбраны очередью заданий.

Задачи здесь — это любой код внутри функции, который необходимо выполнить. Когда операция ввода-вывода, setTimeout, setInterval, setImmediate или задача ОС завершаются, вызывается функция обратного вызова, которая затем вводится в очередь заданий. Он будет «ждать», пока стек полностью не опустеет. Когда стек пуст, он отправит функцию обратного вызова в начале очереди в стек.

Как я упоминал выше, цикл обработки событий похож на цикл for или while. У него есть определенные фазы, через которые он проходит — это называется «Итерация цикла событий».

Каждая из фаз имеет свою собственную очередь/кучу, которая используется циклом событий для отправки/сохранения обратных вызовов для выполнения (в Node.js существует неправильное представление о том, что существует только одна глобальная очередь, в которой обратные вызовы ставятся в очередь для выполнения что не соответствует действительности.).

Фазы:

1. Таймер

Обратные вызовы таймеров (setTimeout, setInterval) в JavaScript хранятся в куче памяти до истечения срока их действия. Если в куче есть таймеры с истекшим сроком действия, цикл событий подхватывает связанные с ними обратные вызовы и переносит их в стек вызовов.

Технически выполнение обратных вызовов таймера контролируется фазой опроса цикла событий (мы увидим это позже в этой статье).

2. Ожидающие обратные вызовы

На этом этапе выполняются определенные обратные вызовы системного уровня. На самом деле вам не нужно беспокоиться об этом этапе (и, честно говоря, о нем не так много информации), но я хотел, чтобы вы знали, что он есть.

3. Простой/Подготовка

На этом этапе цикл обработки событий ничего не делает. Он простаивает и готовится перейти к следующему этапу.

4. Этап опроса

Именно этот этап делает Node.js уникальным. На этом этапе цикл событий отслеживает новые асинхронные обратные вызовы ввода-вывода и выполняет ожидающие обратные вызовы ввода-вывода (fs.read file()). Выполняются почти все обратные вызовы, кроме setTimeout, setInterval, setImmediate и закрытия.

Этап опроса выполняет две основные функции:

  1. Вычисляя, как долго он должен блокировать и опрашивать ввод-вывод, затем
  2. Обработка событий в очереди опроса.

Когда цикл событий переходит в фазу опроса и нет запланированных таймеров(setTimeout, setInterval), происходит одно из двух:

  • Если очередь опроса не пуста, цикл событий будет перебирать свою очередь обратных вызовов, выполняя их синхронно, пока либо очередь не будет исчерпана, либо не будет достигнут жесткий предел, зависящий от системы.
  • Если очередь опроса пуста, произойдет еще одно из двух событий:
  • Если сценарии были запланированы setImmediate(), цикл событий завершит фазу опроса и перейдет к фазе проверки для выполнения этих запланированных сценариев.
  • Если сценарии не были запланированы setImmediate(), цикл обработки событий будет ожидать добавления обратных вызовов в очередь, а затем немедленно их выполнять.

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

5. Отметить/установитьНемедленно

setImmediate() на самом деле является специальным таймером, который запускается в отдельной фазе цикла событий. Он использует API-интерфейс libuv (мы поговорим об этом), который планирует выполнение обратных вызовов после завершения фазы опроса.

6. Закрытие обратных вызовов

На этом этапе цикл событий выполняет обратные вызовы, связанные с закрывающими событиями, такими как socket.on('close', fn) или process.exit().

«libuv» — это библиотека, которая предоставляет набор потоков (пул потоков) для среды выполнения NodeJS для выполнения длительных задач, таких как fs задач на основе модулей. По умолчанию он обеспечивает 4 потока для каждого процесса узла, но вы можете изменить размер пула потоков, установив process.env.UV_THREADPOOL_SIZE на любое значение, которое вы хотите.

«Асинхронные помощники ОС» используются при выполнении любых низкоуровневых операций ОС. Например, вызов REST API с использованием модуля http или https или создание веб-сервера с использованием http.createServer(). Эти операции никогда не используют пул потоков и выполняются немедленно, как только ядро ​​ЦП становится доступным для работы.

После того, как «libuv» или «OS Async Helpers» выполнили свои задачи, они помещают обратные вызовы в «Очередь заданий», чтобы «Цикл событий» забрал их, а затем отправил на «движок V8» для выполнения.

Пример

Вывод

2
1

Давайте посмотрим, как Node.js выполняет этот код:

  1. Когда вызывается main(), fs.readfile() встречается первым с обратным вызовом. Этот обратный вызов помещается в очередь фазы ввода-вывода, потому что операции fs являются операциями ввода-вывода.
  2. Когда цикл событий видит, что операция чтения файла завершена, он начинает выполнение обратного вызова, после чего переходит к фазе Check(setImmediate).

Фаза Проверка предшествует фазе Таймеры. Следовательно, в фазе ввода-вывода обратный вызов setImmediate всегда будет выполняться перед setTimeout(fn, 0).

Основное преимущество использования setImmediate() по сравнению с setTimeout() заключается в том, что setImmediate() всегда будет выполняться перед любыми таймерами, если они запланированы в цикле ввода-вывода, независимо от того, сколько таймеров присутствует.

Спасибо за чтение! Вы можете следить за мной на Medium и LinkedIn. Не стесняйтесь задавать любые вопросы в комментариях.🌼🌼🌼