Мы рассмотрим простым языком, как на самом деле работает Node.js. Все, что вам нужно знать, чтобы создавать эффективные приложения для узлов.

Привилегии

Чтобы продолжить, вам необходимо иметь некоторое представление об асинхронном и бэкэнд-программировании.

Архитектура

Что касается зависимостей Node, в основном есть пара библиотек, от которых Node зависит для правильной работы:

Механизм JavaScript V8 преобразует код JavaScript в машинный код, который компьютер может понять, поскольку JavaScript предназначен для работы в браузерах, а не в операционных системах. (Написано на C ++)

Libuv: библиотека с открытым исходным кодом с упором на асинхронный ввод / вывод. Этот уровень предоставляет узлам доступ ко многим функциям, таким как файловая система, работа в сети и т. д. Кроме того, Libuv реализует две чрезвычайно важные функции Node.js: Цикл событий и Пул потоков, и мы скоро поговорим об этих плохих парнях. (Написано на C)

Итак, Node.js использует преимущества этих мощных библиотек, написанных на C и C ++, и дает нам доступ к их функциям на чистом JavaScript. Это дает нам очень хороший уровень абстракции, чтобы сделать нашу жизнь намного проще, не все из нас любят возиться с языками программирования низкого уровня, верно? 😆 Как это круто! ❤️

Это, как мы уже сказали, основные библиотеки, но есть и другие, которые использует Node: http-parser для разбора http, очевидно, c-ares для некоторых вещей DNS, openSSl для криптографии и zlib для сжатия, и это не так важно понимать.

Потоки и пул потоков

Node.js является однопоточным, поэтому независимо от того, есть ли у вас один пользователь или 10 миллионов пользователей, обращающихся к вашему приложению, есть только один поток, который будет иметь дело с этим, поэтому вам нужно быть очень осторожным, чтобы не блокировать этот поток. Итак, как это работает?

Вот что происходит в нашем потоке, когда мы запускаем приложение Node:

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

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

Цикл событий (наконец)

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

Каждый раз, когда что-то происходит в приложении NodeJS, создается событие.

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

По мере обработки этих событий они создают больше событий. Эти новые события помещаются в конец очереди событий, и цикл продолжается.

Пример

Давайте теперь возьмем пример, объясняющий все это

Скрипт будет работать так:

  1. fs.readFile вызван и находится в очереди событий
  2. Внутренний код readFile () присоединяет «завершенное» событие с обратным вызовом myfunc.
  3. Событие регистрируется в этой операции через libuv
  4. Libuv запрашивает новый поток из своего внутреннего пула потоков
  5. Libuv делегирует чтение файла ввода-вывода потоку и запускает его
  6. Поток завершается и запускает событие «complete»
  7. Наблюдатель событий V8 работает в цикле и проверяет наличие любого нового события в очереди событий
  8. При получении события он получает зарегистрированный обратный вызов и переходит к узлу.
  9. Узел получает обратный вызов и помещает его в очередь обратного вызова
  10. Почти известный цикл задач / событий узла проверяет наличие любого нового обратного вызова в очереди
  11. При получении обратного вызова он пытается запустить обратный вызов, как только стек вызовов пуст.
  12. Обратный вызов myfunc помещается в стек вызовов
  13. Он выполняется, и console.log добавляется в стек вызовов.
  14. Печать отображается в консоли «Задача выполнена».

Бонус: как на самом деле работают необходимые модули?

Чтобы включить модуль в файл, node.js выполняет 5 шагов:

Устранение: Так как существует 3 типа модулей:

Узлу необходимо знать, какой из них получить и откуда, другими словами, ему необходимо разрешить путь к модулю. Узел шагов будет выполнять следующие действия:

1- Начинается с основных модулей.

2- Если путь начинается с ./ или ../, он пытается загрузить модуль разработчика.

3. Если файл не найден, он пытается найти папку с index.js в ней.

4- Перейдите в node_modules / и попробуйте найти там модуль.

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

Обертывание: на этом этапе узел оборачивает код модуля в выражение немедленно вызываемой функции (IIFE), которое дает нам доступ к некоторым объектам. Здесь мы получаем ответ на вопрос Где находится require, и почему у нас есть к нему доступ? Это то, что я называю Magic 💖

Выполнение: выполнение кода модуля.

Возврат экспорта: после выполнения функции мы получаем возврат. В этом случае мы получаем экспорт необходимого модуля (module.exports или exports.funcName).

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

На этом все, спасибо, что подписались, и скоро увидимся! 😄

PS: Если вам нравится то, что я делаю, и вы хотите поддержать меня, вы можете сделать это, став участником среднего уровня, используя эту ссылку