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



Сегодня мы поговорим о том, какими знаниями в области JS нужно обладать для того, чтобы продуктивно разрабатывать приложения для платформы Node.js, обсудим различия браузерного и серверного JavaScript-кода, поговорим о JS-движках и о некоторых приёмах Node.js-разработки.

Какими JS-знаниями нужно обладать для Node.js-разработки?


Предположим, вы только начали заниматься программированием. Насколько глубоко вам нужно изучить JavaScript для успешного освоения Node.js? Начинающему трудно достичь такого уровня, когда он приобретёт достаточную уверенность в своих профессиональных навыках. К тому же, изучая программирование, вы можете почувствовать, что не понимаете точно, где заканчивается браузерный JavaScript и начинается разработка для Node.js.

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

  • Лексические конструкции.
  • Выражения.
  • Типы.
  • Переменные.
  • Функции.
  • Ключевое слово this.
  • Стрелочные функции
  • Циклы
  • Области видимости.
  • Массивы.
  • Шаблонные строки.
  • Применение точки с запятой.
  • Работа в строгом режиме.

На самом деле, этот список можно продолжать, но если вы всё это освоите, это значит, что вы заложите хорошую базу для продуктивной клиентской и серверной разработки на JavaScript.
Следующие концепции языка, кроме того, весьма важны для понимания идей асинхронного программирования, которые являются одной из базовых частей Node.js. В частности, речь идёт о следующем:

  • Асинхронное программирование и функции обратного вызова.
  • Таймеры.
  • Промисы.
  • Конструкция async/await.
  • Замыкания.
  • Цикл событий.

Существует множество материалов по JavaScript, которые позволяют начинающим освоить язык. Например, вот учебный курс автора данного руководства, вот весьма полезный раздел MDN, вот учебник сайта javascript.ru. Изучить базовые механизмы JavaScript можно на freecodecamp.com.

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

Различия между платформой Node.js и браузером


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

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

В браузере основной объём работы приходится на выполнение различных операций с веб-документами посредством DOM, а также — на использование других API веб-платформы, таких, скажем, как механизмы для работы с куки-файлами. Всего этого в Node.js, конечно, нет. Тут нет ни объекта document, ни объекта window, равно как и других объектов, предоставляемых браузером.

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

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

Так как JavaScript крайне быстро развивается, браузеры просто не успевают достаточно оперативно реализовать все его новшества. К тому же, далеко не все пользователи работают на самых свежих версиях браузеров. В результате разработчики, которые хотят использовать в своих программах что-то новое, вынуждены это учитывать, заботиться о совместимости их приложений с используемыми браузерами, что может вылиться в необходимость отказа от современных возможностей JavaScript. Можно, конечно, для преобразования кода в формат, совместимый со стандартом EcmaScript 5, который поддерживают все браузеры, воспользоваться транспилятором Babel, но при работе с Node.js вам это не понадобится.

Ещё одно различие между Node.js и браузерами заключается в том, что в Node.js используется система модулей CommonJS, в то время как в браузерах можно наблюдать начало реализации стандарта ES Modules. На практике это означает, что в настоящее время в Node.js, для подключения внешнего кода, используется конструкция require(), а в браузерном коде — import.

V8 и другие JavaScript-движки


V8 — это название JavaScript-движка, используемого в браузере Google Chrome. Именно он отвечает за выполнение JavaScript-кода, который попадает в браузер при работе в интернете. V8 предоставляет среду выполнения для JavaScript. DOM и другие API веб-платформы предоставляются браузером.

JS-движок независим от браузера, в котором он работает. Именно этот факт сделал возможным появление и развитие платформы Node.js. V8 был выбран в качестве движка для Node.js в 2009 году. В результате прямо-таки взрывного роста популярности Node.js V8 оказался движком, который в наши дни отвечает за выполнение огромного количества серверного JS-кода.

Экосистема Node.js огромна. Благодаря этому V8 также используется, посредством проектов наподобие Electron, при разработке настольных приложений.

Надо отметить, что, помимо V8, существуют и другие движки:

  • В браузере Firefox применяется движок SpiderMonkey.
  • В Safari применяется JavaScriptCore (он ещё называется Nitro).
  • В Edge используется движок Chakra.

Список JS-движков этим не ограничивается.

Эти движки реализуют спецификацию ECMA-262, называемую ещё ECMAScript. Именно эта спецификация стандартизирует JavaScript. Свежую версию стандарта можно найти здесь.

?Разработка JS-движков и стремление к производительности


Движок V8 написан на C++, его постоянно улучшают. Он может выполняться на многих системах, в частности, на Mac, Windows и Linux. Здесь мы не будем говорить о деталях реализации V8. Сведения о них можно найти в других публикациях, в том числе — на официальном сайте V8. Они со временем меняются, иногда — очень серьёзно.

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

?Интерпретация и компиляция


JavaScript считается интерпретируемым языком, но современные движки занимаются далеко не только интерпретацией JS-кода. Они его компилируют. Это веяние можно наблюдать с 2009-го года, когда компилятор JavaScript был добавлен в Firefox 3.5, после чего и другие производители движков и браузеров переняли эту идею.

V8 выполняет компиляцию JavaScript для повышения производительности кода. Со времени появления Google Maps в 2004-м году JavaScript эволюционировал, превратился из языка, на котором, для реализации интерактивных возможностей веб-приложений, обычно писали по несколько десятков строк, в язык, на котором пишут браузерные приложения, состоящие из тысяч или даже сотен тысяч строк кода. Такие приложения могут выполняться в браузере часами, что серьёзно отличается от старых сценариев использования JS, код на котором, например, мог применяться лишь для проверки правильности данных, вводимых в формы. В современных условиях компиляция кода имеет огромный смысл, так как, хотя выполнение этого шага может отложить момент запуска кода, после компиляции код оказывается гораздо более производительным, чем тот, который обрабатывался бы исключительно интерпретатором и запускался бы быстрее, но работал бы медленнее.

Теперь, обсудив некоторые положения, касающиеся JS-движков, интерпретации и компиляции кода, перейдём к практике. А именно, поговорим о том, как завершать работу Node.js-приложений.

Выход из Node.js-приложения


Существует несколько способов завершения работы Node.js-приложений.

Так, при выполнении программы в консоли, завершить её работу можно, воспользовавшись сочетанием клавиш ctrl+c. Но нас больше интересуют программные способы завершения работы приложений. И начнём мы, пожалуй, с самой грубой команды выхода из программы, которую, как вы сейчас поймёте, лучше не использовать.

Модуль ядра process предоставляет удобный метод, который позволяет осуществить программный выход из Node.js-приложения. Выглядит это так:

process.exit()

Когда Node.js встречает в коде такую команду, это приводит к тому, что его процесс мгновенно завершается. Это означает, что абсолютно всё, чем занималась программа, будет довольно грубо и безусловно прервано. Речь идёт о невызванных коллбэках, о выполняемых в момент выхода сетевых запросах, о действиях с файлами, об операциях записи в stdout или stderr.

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

process.exit(1)

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

Код выхода, кроме того, можно назначить свойству process.exitCode. Выглядит это так:

process.exitCode = 1

После того, как программа завершит работу, Node.js вернёт системе этот код.

Надо отметить, что работа программы самостоятельно завершится естественным образом после того, как она выполнит все заданные в ней действия. Однако в случае с Node.js часто встречаются программы, которые, в идеальных условиях, рассчитаны на работу неопределённой длительности. Речь идёт, например, об HTTP-серверах, подобных такому:

const express = require('express')
const app = express()
app.get('/', (req, res) => {
  res.send('Hi!')
})
app.listen(3000, () => console.log('Server ready'))

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

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

Обратите внимание на то, что для использования объекта process не нужно ничего подключать с помощью require, так как этот объект доступен Node.js-приложениям по умолчанию.

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

const express = require('express')
const app = express()
app.get('/', (req, res) => {
  res.send('Hi!')
})
app.listen(3000, () => console.log('Server ready'))
process.on('SIGTERM', () => {
  app.close(() => {
    console.log('Process terminated')
  })
})

Что такое «сигналы»? Сигналы — это средства взаимодействия процессов в стандарте POSIX (Portable Operating System Interface). Они представляют собой уведомления, отправляемые процессу для того, чтобы сообщить ему о неких событиях.

Например, сигнал SIGKILL сообщает процессу о том, что ему нужно немедленно завершить работу. Он, в идеале, работает так же, как process.exit().

Сигнал SIGTERM сообщает процессу о том, что ему нужно осуществить процедуру нормального завершения работы. Подобные сигналы отправляются из менеджеров процессов, вроде upstart или supervisord, и из многих других.

Отправить такой сигнал можно и из самой программы, воспользовавшись следующей командой:

process.kill(process.pid, 'SIGTERM')

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

Чтение переменных окружения из Node.js


Модуль ядра process имеет свойство env, которое даёт доступ ко всем переменным окружения, которые были заданы на момент запуска процесса.

Вот пример работы с переменной окружения NODE_ENV, которая, по умолчанию, установлена в значение development:

process.env.NODE_ENV // "development"

Если, до запуска скрипта, установить её в значение production, это сообщит Node.js о том, что программа выполняется в продакшн-окружении.

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

Итоги


Сегодня мы коснулись вопросов браузерного и серверного программирования на JavaScript, поговорили о JS-движках, о том, как завершать работу серверных приложений, и о том, как читать переменные среды из Node.js-программ. В следующий раз мы расскажем о хостингах для Node.js-приложений, о том, как пользоваться Node.js в режиме REPL, о работе с аргументами, которые можно передавать скриптам при их вызове, о взаимодействии с консолью, и об оформлении кода в виде модулей.

Уважаемые читатели! Какие учебные материалы по JavaScript вы посоветовали бы начинающим?

Комментарии (1)


  1. ixolit
    14.09.2018 19:34

    Спасибо, продолжайте пожалуйста.