Суть Node.js

Опубликовано: 18 Декабря, 2021

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

Вот некоторые примеры модулей, присутствующих в ядре узла:

  • Для работы с файловыми системами у нас есть модуль fs.
  • Для сетей у нас есть модуль http.
  • Для получения информации об ОС у нас есть модуль под названием os .

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

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

Обратные вызовы: обратные вызовы - одна из самых важных основ, которые вам нужно понять, чтобы освоить Node. Перед этим давайте посмотрим, зачем нам нужны обратные вызовы и как они работают в Node.

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

Узел имеет неблокирующую модель ввода-вывода, потому что узел асинхронен по конструкции. Серверы, созданные с помощью Node, когда получают запрос, обрабатывают его и возвращают ответ, как традиционные серверы. Но сервер Node может выполнять другие задачи одновременно, когда запрос находится в периоде обработки.

Пример: создайте новую папку, а затем создайте в ней файл learn-callback.js и файл name.txt . Наша цель - вывести на терминал настроенное приветствие и шаблон цикла. Введите свое имя в name.txt и сохраните файл. В нашем файле есть « GeeksforGeeks».

  • Синхронная версия традиционного сервера:
    // Tell node we need to work with filesystem
    const fs = require( "fs" );
    // Read the file contents "synchronously" in
    // string (utf-8) encoding
    const fileContents = fs.readFileSync( "name.txt" , "utf-8" );
    // Print to console
    console.log( "Hello, " , fileContents);
    // Print pattern
    for (let i = 0; i < 5; i++) console.log(i);

    Выход:

    Привет, GeeksforGeeks
    0
    1
    2
    3
    4
    
  • Асинхронная версия Node: откройте свой терминал в каталоге, в котором сохранены ваши файлы. Запустите код с помощью node learn-callback.js и посмотрите на результат. вы перейдете к сути, но сначала посмотрите версию Node.
    // Tell node we need to work with filesystem
    const fs = require( "fs" );
    // Read the file contents "asynchronously" in
    // string (utf-8) encoding
    fs.readFile( "name.txt" , "utf-8" , (error, fileContents) => {
    if (error)
    error; return
    else
    console.log( "Hello, " , fileContents);
    });
    // Print the pattern
    for (let i = 0; i < 5; i++)
    console.log(i);

    Выход:

    0
    1
    2
    3
    4
    Привет, GeeksforGeeks
    

Объяснение: Запустите код с помощью node learn-callback.js . Вы заметили разницу в выводах? Это связано с неблокирующей моделью Node. В синхронном варианте мы сначала наблюдаем привет, затем паттерн. Мы запускаем запрос на чтение файла name.txt, файл обрабатывается, печатается hello, а затем печатается шаблон. В синхронной модели выполнение является последовательным, т.е. в порядке сверху вниз.

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

В выделенном выше коде fs.readFile сообщает узлу прочитать файл name.txt в кодировке utf-8. Третий аргумент fs.readFile - это обратный вызов. Обратные вызовы - это функции, которые выполняются после завершения определенного процесса. Когда файл читается, узел свободен и выполняет следующую строку кода сразу после функции fs.readFile , которая в нашем случае является циклом, поэтому цикл выполняется во время процесса чтения, и мы получаем шаблон в Терминал. Когда чтение завершено, выполняется функция обратного вызова, и после шаблона в терминале печатается привет.
Таким образом, обратные вызовы - это функции, которые выполняются позже, после того, как процесс завершил выполнение, и в этот период узел может выполнять другие задачи. Имейте в виду, что обратные вызовы не являются особенностями узла. Фактически, они встроены в JavaScript. Node просто использует его с умом для достижения неблокирующего характера ввода-вывода.



События: вы можете думать о таких событиях, как «когда X происходит с Y». Таким образом, в этой аналогии «X» - это событие, которое генерируется узлом, а «Y» - это слушатель, который ожидает сигнала «X», чтобы выполнить свою работу. Напишем небольшую программу, чтобы понять эту концепцию.

  • Пример: этот пример иллюстрирует события. Запустите этот код и посмотрите, получите ли вы правильный результат.
    // Require "events"; give us access to EventEmitter class
    // EventEmitter class has all the event related methods in it
    const EventEmitter = require( "events" );
    // Create an instance of the EventEmitter class
    const ourEmitter = new EventEmitter();
    // Create an event listener - listens for the "GfG opened" event
    // Event listeners always keep its ear open; it never sleeps
    // Means it'll keep on listening for the event throughout the code
    // It'll execute the callback function when "GfG opened" event is emitted
    ourEmitter.on( "GfG opened" , (error) => {
    if (error)
    error; return
    else
    console.log( "Let's learn computer science concepts." );
    });
    // Emit event or send a signal that "GfG opened" has happened
    ourEmitter.emit( "GfG opened" );

    Выход:

    Давайте изучим концепции информатики.
    

    Объяснение: Когда вы генерируете событие «GfG open», у нас есть прослушиватель событий, который выполняет функцию обратного вызова, которая выводит сообщение на консоль. Теперь посмотрим, что происходит, когда мы помещаем ourEmitter.emit («GfG открыт»); перед слушателем событий.

  • Программа, в которую помещаем ourEmitter.emit («GfG открыт»); перед слушателем событий:
    ...
    // Emit "GfG opened"
    ourEmitter.emit( "GfG opened" );
    // Create an event listener
    ourEmitter.on( "GfG opened" , (error) => {
    if (error)
    error; return
    else
    console.log( "Let's learn computer science concepts." );
    ...

    Вывод: API событий узла говорит:

     "Когда объект EventEmitter генерирует событие, все функции, прикрепленные к 
    это конкретное событие вызывается синхронно "

    Это означает, что когда узел испускает событие «GfG open», узел проверяет, прослушивает ли кто-нибудь это событие, но узел еще не знает о прослушивателе, так как прослушиватель находится после команды emit. .emit Node event .emit не может проверить прослушиватели, которые появляются после команды emit, потому что она синхронна . Так что порядок кода важен, когда вы имеете дело с событиями. Эмпирическое правило: сначала слушайте, затем испускайте. Сначала создайте слушателя, а затем испустите событие.

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

    Потоки: для чтения и записи данных используются два подхода: с буферизацией и потоками . В буферизованном подходе все данные должны быть прочитаны до начала процесса записи. Но потоки намного эффективнее. Потоки читают фрагмент данных, в это время другой поток может продолжать запись предыдущего фрагмента данных. Таким образом, node.js обрабатывает данные асинхронно, выполняя задачи параллельно .

    Ручьи идут с обвязкой. В основном то, что делает конвейер, - это то, что они принимают вывод потока, и этот вывод может быть отправлен в другой поток через конвейер, который становится входом для этого нового потока. Это дает узлу удивительные возможности. Потоки экономят время, потому что они не тратят время на ожидание, пока все чтение произойдет сразу, вместо этого мы постоянно читаем и записываем одновременно. Да, мы делаем это асинхронно, как говорится.

    Кроме того, потоки пространственно эффективны (экономят место в памяти). Предположим, нам нужно прочитать файл размером 100 МБ и записать его где-нибудь, и у нас есть буфер размером 50 МБ. Буферизованный подход сначала должен прочитать все данные, а затем начать их запись. При буферизированном подходе, когда начинается процесс чтения, наш буфер в конечном итоге выйдет из строя, как только мы превысим отметку в 50 МБ.

    Но мы можем использовать потоки для чтения блока данных размером 50 МБ, записи этих данных и затем очистки этого буфера перед тем, как перейти к чтению следующих 50 МБ. Так что в случае с потоком утечек не будет.