![](https://habrastorage.org/webt/s-/7a/cf/s-7acfzhsw55rupodlj9zl6_3m8.jpeg)
Всем привет! Совсем скоро стартует курс «Разработчик Node.js», в связи с чем мы провели традиционный открытый урок. На вебинаре рассмотрели сильные и слабые стороны Node, а также обсудили, для каких задач эта программная платформа подходит лучше всего, а для каких следует выбирать другие языки и фреймворки. И, разумеется, не обошлось без практики. Для запуска примеров и программ необходимо было установить Node.js.
Преподаватель — Александр Коржиков, Dev IT Engineer в компании ING Group (Нидерланды).
![](https://habrastorage.org/webt/gy/xr/it/gyxritf2vnerlxqhnevsput7s4q.png)
Несколько слов о Node.js
Node.js — это асинхронная среда исполнения JavaScript, в основе которой находятся такие понятия, как Event Loop и событийно-ориентированная архитектура. Платформа Node.js и стандартный пакетный менеджер NPM позволяют создавать эффективные приложения для различных предметных областей — от Web и до Machine Learning.
Самый простой пример web-сервера (
node server.js
):const http = require('http')
const hostname = '127.0.0.1'
const port = 3000
const server = http.createServer((req, res) => {
res.statusCode = 200
res.setHeader('Content-Type', 'text/plain')
res.end('Hello World\n')
})
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`)
})
Глядя на код выше, можно отметить следующие особенности Node.js:
- Мы можем запустить JavaScript-код прямо на сервере, то есть возможно исполнение JavaScript-файлов с помощью команды node.
- Поддерживается CommonJS-формат модулей для загрузки зависимостей и ES Modules, причём можно использовать оба формата.
- Поддерживается стандартная библиотека модулей, частью которой является HTTP.
- API основано на асинхронном паттерне Callbacks, но также поддерживаются Promise.
- Поддерживается синтаксис ES2015. Кстати, вот полезная ссылка, с помощью которой всегда можно посмотреть, в какой версии Node какие функции JS поддерживаются. Если что-то идёт не так, можно сравнить и понять, в чём проблема.
Также не пропустите небольшое демо (VSCode + Chrome Debug).
Краткий экскурс в историю
Платформа Node.js появилась в 2009 году, основным разработчиком и создателем проекта был Ryan Dahl. Его главная идея заключалась в создании неблокирующего I/O (input-output) для сервера, причём с использованием JavaScript. В то время такие подходы были не очень распространены, и JavaScript был одним из наилучших решений.
Node.js использует внутри себя движок Chromium, точнее, ту его часть, которая интерпретирует JavaScript — V8. Именно благодаря тому, что интерпретатор в своё время отделился в отдельный проект V8, стало возможным просто создать экосистему вокруг него. Собственно говоря, именно это и сделал создатель Node.js Ryan Dahl. Кстати, сегодня существует Node.js Foundation — организация, которая занимается поддержкой проекта.
![](https://habrastorage.org/webt/sy/ia/yr/syiayrq85tpdajr8whmtfysla6c.png)
Структура
Во-первых, библиотека написана на C++ и JavaScript. Сам проект лежит на GitHub, поэтому, если любопытно, можете ознакомиться с его исходным кодом. Углубляться в его изучение можно очень долго)).
Во-вторых, как уже было сказано, в неё входит V8 (платформа исполнения JavaScript от Google) и libuv как часть Event Loop (асинхронный событийный цикл).
Ну и, разумеется, присутствуют различные модули для работы с операционной системой, позволяющие выполнять те бизнес-задачи, которые вам нужны.
Также стоит сказать про основные паттерны проектирования, которые обычно используются в Node.js:
- Callback (его уже упоминали);
- Observer (более простой событийный паттерн);
- Module (хотя он сейчас организован и в самом языке JavaScript, в Node.js он до сих пор актуален);
- Reactor (паттерн асинхронного взаимодействия с какими-то ресурсами, когда вы не блокируете основной поток кода). Этот паттерн хорошо знаком JS-разработчикам. К примеру, если мы работаем в браузере и пишем фронтенд, мы не блокируем всю работу браузера, а просто подписываемся на события от пользователя, и, когда событие наступает (после «клика», «enter» и т. д.), мы выполняем наш код.
Кстати, вот вам небольшое соревнование, которое провели на вебинаре)). И двигаемся дальше.
Модули стандартного дистрибутива Node
![](https://habrastorage.org/webt/lz/vc/dt/lzvcdthe1hqpmlr_jhnltxu2ewi.png)
Знаете ли вы, какие модули включены в стандартный дистрибутив Node? То есть речь идёт о модулях, которые уже встроены, следовательно, их можно не устанавливать. Вообще, их около 50, давайте перечислим основные. Для удобства восприятия условно разделим их на 5 пунктов:
1. Main (для обычных операций):
- fs;
- timers;
- streams (для работы с потоками).
2. Utilities:
- path (для работы с путями);
- util;
- zlib (архивация и разархивация);
- crypto (для криптографических функций).
3. Processes (всё, что касается многопроцессности и параллельности):
- child_process (основной модуль запуска второстепенных модулей, предоставляет большие возможности именно по запуску и слежению процессов);
- cluster (похож на первый, но позволяет распараллеливать задачи на ядрах вашего процессора);
- worker_threads (реализация нитей или потоков, но она не совсем поточная, а почему, лучше читать в документации).
4. Protocols (всевозможные протоколы):
- http(s);
- net;
- dns.
5. System (соответственно, системные модули, включая отладочные):
- os;
- v8;
- async_hooks;
- perf_hooks;
- trace_events.
Что ещё добавить:
— globals объект — аналог window;
— все объекты JavaScript, которые доступны в браузере, также доступны и в Node.js;
— timeouts — почти как в браузере;
— process — репрезентация текущего процесса;
— доступна console.
Callbacks
Callback — функция, переданная в качестве аргумента коду, который предполагает исполнить его в какой-то момент времени. Кстати, люди редко задумываются о том, что их исполнение может быть синхронным или асинхронным.
const server = http.createServer((req, res) => {
res.statusCode = 200
res.setHeader('Content-Type', 'text/plain')
res.end('Hello World\n')
})
server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`)
})
В Node по умолчанию callback выполняется с «ошибкой» и результатом асинхронно:
fs.readFile('/etc/passwd', (err, data) => {
if (err) throw err
console.log(data)
})
А вот как выглядит антипаттерн — наглядный пример того, как неправильно использовать Callbacks. Как говорится, классический Callback Hell:
fs.readdir(source, function (err, files) {
if (err) {
console.log('Error finding files: ' + err)
} else {
files.forEach(function (filename, fileIndex) {
console.log(filename)
gm(source + filename).size(function (err, values) {
if (err) {
console.log('Error identifying file size: ' + err)
} else {
console.log(filename + ' : ' + values)
aspect = (values.width / values.height)
widths.forEach(function (width, widthIndex) {
height = Math.round(width / aspect)
console.log('resizing ' + filename + 'to ' + height + 'x' + height)
this.resize(width, height).write(dest + 'w' + width + '_' + filename, function(err) {
if (err) console.log('Error writing file: ' + err)
})
}.bind(this))
}
})
})
}
})
Теперь давайте посмотрим, как создать простой веб-сервер и отдать индекс HTML в виде ответа клиента:
const http = require('http')
const server = http.createServer((req, res) => {
res.statusCode = 200
res.setHeader('Content-Type', 'text/plain')
res.end('Hello World\n')
})
Чтобы прочитать локальный html-файл:
const fs = require('fs')
fs.readFile('./index.html', (err, text) => {
console.log(text)
})
Заканчивая тему колбэков, хотелось бы упомянуть про Callback Types. Бывают колбэки, которые возвращают только ошибку, если есть ошибка:
fs.access('/etc/passwd', fs.constants.R_OK, (err) => {
console.log(err ? 'no access!' : 'read')
})
Также сейчас всё больше и больше модулей и API используют Promises из коробки, и это хорошо. Node поддерживает Promises:
util.promisify()
fs.promises.*
Тип возвращаемого значения:
http.request('https://example.com', (error, response, body) => {
...
})
Node Q&A
При написании веб-серверов часто используют Express, хотя есть много других библиотек и фреймворков, реализующих похожий функционал. Тем не менее Express является наиболее популярным и стабильным. Можно сказать, это стандарт для написания серверов.
import express from "express";
const app = express();
const port = 3000;
app.get("/", (req, res) => {
res.send("Hello World!");
});
app.listen(port, () => {
console.log(`Example app listening
});
![](https://habrastorage.org/webt/1x/ml/lo/1xmllok6fpyj1q_82zod2tzl5du.png)
Что же, теперь можете самостоятельно попробовать создать веб-сервер с Express-генератором:
- с использованием jade, cookie-session;
- сделав наивный login logout.
Или сразу посмотрите, как это сделал преподаватель, подготовивший уже готовое решение на TypeScript.
На этом всё. Напоследок — парочка полезных ссылок:
- Официальная документация
- Руководство
- Фреймворки для Node.js: ExpressJS и NestJS
И до встречи на курсе.