Статьи

Новичок в Node.js

Сейчас 3 часа утра. У вас есть руки над клавиатурой, глядя на пустую консоль. Яркая заставка на темном фоне готова, желая принять команды. Хотите ненадолго взломать Node.js?

Одна из замечательных особенностей Node.js — он работает где угодно. Это открывает различные возможности для экспериментов со стеком. Для любого опытного ветерана это увлекательный инструментарий командной строки. Что особенного, так это то, что мы можем исследовать стек внутри защитной сетки командной строки. И здорово, что мы все еще говорим о JavaScript — поэтому у большинства читателей, знакомых с JS, не должно возникнуть проблем с пониманием того, как все это работает. Итак, почему бы не запустить node в консоли?

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

Node.js в консоли Windows

Почему Node.js?

Прежде чем мы начнем, давайте рассмотрим лакомые кусочки, которые выделяют Node.js из толпы:

  • он предназначен для неблокирующего ввода / вывода
  • он предназначен для асинхронных операций
  • он работает на движке Chrome V8 JavaScript.

Возможно, вы слышали эти моменты во многих источниках, но что все это значит? Вы можете думать о Node.js как о движке, который предоставляет множество API для языка JavaScript. В традиционных вычислениях, где процессы являются синхронными, API ожидает выполнения следующей строки кода при выполнении любой операции ввода-вывода. Операция ввода / вывода — это, например, чтение файла или выполнение сетевого вызова. Node.js не делает этого; с самого начала она была разработана для асинхронных операций. На современном компьютерном рынке это имеет огромное преимущество. Можете ли вы вспомнить, когда в последний раз покупали новый компьютер, потому что у него был более быстрый однопроцессорный? Количество ядер и более быстрый жесткий диск важнее.

В оставшейся части этой статьи, когда вы видите > , который является символом приглашения, это означает, что вы должны нажать Enter, чтобы ввести следующую команду. Более того, перед запуском кода в этой статье вы должны открыть CLI и выполнить командный node . С этим сказал, давайте начнем наш тур!

Callbacks

Чтобы начать, введите эту функцию:

 > function add(a, b, callback) { var result = a + b; callback(result); } 

Новичку обратный вызов в JavaScript может показаться странным. Это, конечно, не похоже ни на один классический подход ООП. В JavaScript функции — это объекты, а объекты могут принимать другие объекты в качестве параметров. JavaScript не заботится о том, что имеет объект, поэтому из этого следует, что функция может принимать объект, который оказывается еще одной функцией. Арность , которая является числом параметров, переходит от двух в add() к одному параметру в обратном вызове. Эта система обратных вызовов является мощной, поскольку она позволяет скрывать инкапсуляцию и реализацию.

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

Чтобы поиграть с add :

 > add(2, 3, function (c) { console.log('2 + 3 = ' + c) }); > add(1, 1, function (c) { console.log('Is 1 + 1 = 3? ' + (c === 3)); }); 

Есть много более творческих способов поиграть с обратными вызовами. Обратные вызовы являются строительными блоками для некоторых важных API в Node.js.

Асинхронные операции

С помощью обратных вызовов мы можем начать создавать асинхронные API. Например:

 > function doSomething (asyncCallback) { asyncCallback(); } > doSomething(function () { console.log('This runs synchronously.'); }); 

Этот конкретный пример имеет синхронное выполнение. Но у нас есть все, что нужно для асинхронности в JavaScript. asyncCallback , asyncCallback может задерживаться в том же потоке:

 > function doSomething (asyncCallback) { setTimeout(asyncCallback, Math.random() + 1000); } > doSomething(function () { console.log('This runs asynchronously.'); }); console.log('test'); 

Мы используем setTimeout чтобы задержать выполнение в текущем потоке. Тайм-ауты не гарантируют время выполнения. Мы Math.random() чтобы сделать его еще более doSomething() , и вызываем doSomething() , а затем console.log('test') , чтобы отобразить отложенное выполнение. Вы испытаете небольшую задержку от одной до двух секунд, а затем увидите всплывающее сообщение на экране. Это показывает, что асинхронные обратные вызовы непредсказуемы. Node.js помещает этот обратный вызов в планировщик и продолжает свой веселый путь. Когда срабатывает таймер, Node.js начинает с того места, где происходит выполнение, и вызывает обратный вызов. Таким образом, вы должны сосредоточиться вокруг раздражительных обратных вызовов, чтобы понять Node.js.

Короче говоря, обратные вызовы не всегда то, что они кажутся в JavaScript.

Давайте продолжим с кое-чем круче — например, простым поиском DNS в Node.js:

 > dns.lookup('bing.com', function (err, address, family) { console.log(' Address: ' + address + ', Family: ' + family + ', Err: ' + err); }); 

Обратный вызов возвращает объекты err , address и family . Важно то, что возвращаемые значения передаются в качестве параметров обратному вызову. Так что это не похоже на ваш традиционный API var result = fn('bing.com'); , В Node.js вы должны получить обратные вызовы и асинхронность, чтобы получить общую картину. (За подробностями обращайтесь к API-интерфейсу DNS Node.js. ) Вот как DNS lookupc может выглядеть в консоли:

Node.js DNS поиск

Файловый ввод / вывод

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

 > var fs = require('fs'); > fs.writeFile('message.txt', 'Hello Node.js', function () { console.log('Saved.'); }); console.log('Writing file...'); 

Поскольку операция асинхронная, вы увидите «Запись файла…», прежде чем файл будет сохранен на диске. Естественное использование функций обратного вызова хорошо вписывается в этот API. Как насчет чтения из этого файла? Можете ли вы догадаться, как это сделать в Node.js? Мы дадим вам подсказку: обратный вызов принимает err и data . Попробуйте.

Вот ответ:

 > fs.readFile('message.txt', function(err, data) { console.log(data); }); 

Вы также можете передать параметр encoding чтобы получить содержимое файла utf-8 :

 > fs.readFile('message.txt', {encoding: 'utf-8'}, function(err, data) { console.log(data); }); 

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

Веб-сервер

Итак, как насчет веб-сервера? Любая хорошая версия Node.js должна запускать веб-сервер. Представьте себе API с именем createServer с обратным вызовом, который принимает request и response . Вы можете изучить HTTP API в документации. Можете ли вы думать о том, как это выглядит? Вам понадобится модуль http . Идите и начните печатать в консоли.

Вот ответ:

 > var http = require('http'); > var server = http.createServer(function (request, response) { response.end('Hello Node.js'); }); 

Сеть основана на клиент-серверной модели запросов и ответов. Node.js имеет объект request который приходит от клиента, и объект response от сервера. Таким образом, стек охватывает суть Интернета с помощью этого простого механизма обратного вызова. И конечно, это асинхронно. То, что мы здесь делаем, не сильно отличается от файлового API. Мы вводим модуль, говорим ему сделать что-то и передаем обратный вызов. Обратный вызов работает как делегат, который выполняет определенную задачу, учитывая список параметров.

Конечно, все это чепуха, если мы не видим ее в браузере. Чтобы это исправить, введите в командной строке следующее:

 server.listen(8080); 

Укажите ваш любимый браузер на localhost:8080 , который в моем случае был Edge.

Node.js в Microsoft Edge

Представьте, что объект request имеет массу доступной вам информации. Чтобы перемонтировать server , давайте сначала его отключим:

 > server.close(); > server = http.createServer(function (request, response) { response.end(request.headers['user-agent']); }); server.listen(8081); 

Укажите в браузере localhost:8081 . Объект headers предоставляет вам информацию о user-agent которая поступает из браузера. Мы также можем headers объект headers :

 > server.close(); > server = http.createServer(function (request, response) { Object.keys(request.headers).forEach(function (key) { response.write(key + ': ' + request.headers[key] + ' '); }); response.end(); }); server.listen(8082); 

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

 > server.close(); 

Итак, вот оно, создание веб-серверов через командную строку. Я надеюсь, вам понравилось это психоделическое путешествие по node .

Async Await

В ES 2017 введены асинхронные функции. Асинхронные функции — это, по сути, более чистый способ работы с асинхронным кодом в JavaScript. Async / Await был создан, чтобы упростить процесс работы и написания связанных обещаний. Вы, наверное, уже видели, каким может стать нечитаемый цепной код.

Создать async функцию довольно просто. Вам просто нужно добавить ключевое слово async перед функцией:

 async function sum(a,b) { return a + b; } 

Давайте поговорим об await . Мы можем использовать await если хотим заставить остальную часть кода ждать, пока этот Promise разрешит и вернет результат. Await работает только с Promises; это не работает с обратными вызовами. Кроме того, await может использоваться только внутри async функции.

Рассмотрим код ниже, который использует Promise для возврата нового значения через одну секунду:

 function tripleAfter1Second(number) { return new Promise(resolve => { setTimeout(() => { resolve(number * 3); }, 1000); }); } 

При использовании then наш код будет выглядеть так:

 tripleAfter1Second(10).then((result) => { console.log(result); // 30 } 

Далее мы хотим использовать async / await. Мы хотим заставить наш код ждать утроенного значения, прежде чем делать какие-либо другие действия с этим результатом. Без ключевого слова await в следующем примере мы получили бы ошибку, сообщающую нам, что невозможно взять модуль ‘undefined’, потому что у нас пока нет нашего утроенного значения:

 const finalResult = async function(number) { let triple = await tripleAfter1Second(number); return triple % 2; } 

Последнее замечание по async / await: остерегайтесь необработанных ошибок. Используя цепочку then , мы можем завершить ее с помощью catch чтобы перехватить любые ошибки, возникающие во время выполнения. Однако await этого не обеспечивает. Чтобы убедиться, что вы перехватываете все ошибки, рекомендуется окружить ваше предложение await блоком try … catch :

 const tripleResult = async function(number) { try { return await tripleAfter1Second(number); } catch (error) { console.log("Something wrong: ", error); } } 

Для более глубокого изучения async / await, посмотрите Упрощение асинхронного кодирования с асинхронными функциями .

Вывод

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

Это привлекательно, что суть стека не так нова. С самого детства Интернет строился на основе легких, доступных модулей. Если у вас есть время, обязательно прочитайте принципы дизайна Тима Бернерса-Ли. Принцип наименьшей мощности применим к Node.js, учитывая выбор использования JavaScript.

Надеюсь, вам понравился этот инструмент для командной строки. Счастливого взлома!

Эта статья была рецензирована Раби Кираном . Спасибо всем рецензентам SitePoint за то, что сделали контент SitePoint как можно лучше!