Хабрахабр

[Перевод] Почему человек из мира Java стал горячим сторонником Node.js и JavaScript?

Дэвид Хэррон, автор материала, перевод которого мы публикуем сегодня, задался следующим вопросом: «Должен ли человек, работавший более 10 лет в Sun Microsystems, в команде Java SE, до последнего вздоха думать лишь о байт-коде Java и создавать экземпляры абстрактных интерфейсов?». Он задавал этот вопрос применительно к себе, и для него платформа Node.js, после Java, оказалась подобна глотку свежего воздуха. Дэвид говорит, что когда он был уволен из Sun в январе 2009 года (прямо перед поглощением этой компании Oracle), он узнал о Node.js. Эта технология его зацепила. Что значит «зацепила»? С 2010-го года он много писал о программировании для Node.js. А именно, написал несколько книг, в том числе — «Node.js Web Development», четвёртое издание которой вышло в этом году. Он подготовил множество небольших материалов о Node.js, опубликованных в интернете. Фактически, он уделил очень много времени и сил, рассказывая о платформе Node.js и о возможностях JavaScript. Почему того, кто раньше занимался исключительно Java, так увлекли Node.js и JavaScript?

image

О мире Java

Работая в Sun, я верил в технологию Java. Я делал доклады на JavaONE, участвовал в разработке класса java.awt.Robot, занимался организацией мероприятия Mustang Regressions Contest (это был конкурс, направленный на поиск ошибок в Java 1.6), помогал в запуске проекта «Distributions License for Java», который служил ответом на вопрос о Linux-дистрибутивах JDK до появления OpenJDK. Позже я играл некоторую роль в запуске проекта OpenJDK. Попутно я, в течение примерно 6 лет, публиковал материалы в блоге на java.net (теперь этот сайт закрыт). Это были 1-2 статьи в неделю, посвящённые значимым событиям в экосистеме Java. Значительную роль в моей деятельности играла защита Java от тех, кто предрекал этой технологии мрачное будущее.

Мне она досталась после того, как я организовал Mustang Regressions Contest
Эту награду, Duke Award, давали особо отличившимся сотрудникам Sun.

Собственно говоря, тут я и хочу рассказать о том, как я превратился из приверженца Java в горячего сторонника Node.js и JavaScript. Что произошло с человеком, который так много занимался всем тем, что связано с Java?

Я, за последние 3 года, написал довольно много кода на Java, пользовался Spring и Hibernate. Надо сказать, что то, что со мной произошло, нельзя назвать полным отказом от Java. Хотя то, что я теперь делаю в этой области, мне очень нравится (я работаю в индустрии солнечной энергетики, занимаюсь тем, чем мне заниматься приятно, например — пишу запросы для работы с данными из сферы энергетики), программирование на Java в моих глазах теперь лишилось былого великолепия.

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

Вот, вкратце, основные идеи, которых я коснусь в этом материале:

  • Программы на Java полны шаблонного кода, который скрывает намерения программиста.
  • Работа со Spring и Spring Boot дала мне хороший урок, который заключается в том, что попытки скрыть сложные механизмы приводят к появлению ещё более сложных конструкций.
  • Платформа Java EE была проектом, созданным, так сказать, «всеобщими усилиями», который покрывает абсолютно все нужды разработки корпоративных приложений. В результате платформа Java EE оказалась непомерно сложной.
  • Разработка с использованием Spring — это, до определённого момента, занятие приятное. Эта иллюзия исчезает в тот день, когда из глубин некоей подсистемы, о которой вы никогда не слышали, всплывает исключение, которое совершенно невозможно понять, и на то, чтобы выяснить, в чём же заключается проблема, уходит не меньше трёх дней.
  • Какие вспомогательные механизмы, создающие излишнюю нагрузку на систему, нужны фреймворку, который умеет «писать» код за программистов?
  • Хотя IDE вроде Eclipse — это мощные приложения, они являются показателем сложности экосистемы Java.
  • Платформа Node.js появилась в результате усилий одного человека, направленных на совершенствование его видения легковесной архитектуры, управляемой событиями.
  • Кажется, что сообщество JavaScript с энтузиазмом воспринимает идеи избавления от шаблонного кода, что позволяет программистам максимально ясно выражать свои намерения.
  • В качестве решения проблемы ада коллбэков в JS выступает конструкция async/await, которая является примером отказа от шаблонного кода и способствует ясности выражения намерений программистов.
  • Программирование для Node.js — это сплошное удовольствие.
  • В JavaScript нет строгой типизации, характерной для Java. Это — благословение и проклятие языка. Это позволяет легче писать код, но, для того, чтобы проверить его правильность, приходится уделять тестированию больше времени.
  • Системой управления пакетами, представленной npm/yarn, легко и приятно пользоваться. Она не идёт ни в какое сравнение с Maven.
  • И Java, и Node.js предлагают отличную производительность. Это идёт вразрез с мифом, в соответствии с которым JavaScript — это медленный язык, использование которого приводит к низкой производительности платформы Node.js.
  • Производительность Node.js опирается на усилия Google по совершенствованию V8, движка, от которого зависит скорость работы браузера Chrome.
  • Жестокая конкуренция между производителями браузерных JS-движков способствует развитию JavaScript, а это очень выгодно Node.js.

О проблемах разработки на Java

Некоторые инструменты или объекты являются результатом многолетних усилий инженеров по их совершенствованию. Программисты испытывают разные идеи, убирают ненужные атрибуты, и в результате у них получаются сущности, в которых имеется исключительно то, что нужно для решения некоей задачи. Часто таким технологиям присуще нечто вроде весьма привлекательной простоты, скрывающей мощные возможности. К Java это не относится.

Spring — это популярный фреймворк для разработки веб-приложений, основанных на Java.

Программист, который пользуется Spring, не должен, для того, чтобы создать готовую систему, заботиться о сервлетах, о системах постоянного хранения данных, о серверах приложений, и ещё неизвестно о чём. Основная цель Spring, и, в частности, Spring Boot, заключается в предоставлении возможности пользоваться заранее настроенным стеком Java EE. Например, механизмы JPARepository ответственны за генерирование запросов к базам данных для методов, названия которых выглядят как findUserByFirstName. Все эти заботы перекладываются на плечи Spring, а программист занимается написанием кода, реализующего логику приложения. Достаточно передать системе описание метода, а всё остальное сделает Spring. Код таких методов программисту писать не приходится.

Звучит всё это очень хорошо, работать в таком стиле приятно, но — до тех пор, пока не случится какая-нибудь неожиданность.

Что бы это могло значить? Я имею в виду ситуацию, когда, например, приходит исключение Hibernate PersistentObjectException с сообщением detached entity passed to persist. Как оказалось, если описать всё очень упрощённо, это значит, что JSON-данные, поступившие в конечную точку REST, имеют поля ID с некими значениями. На то, чтобы это выяснить, ушло несколько дней. Существуют тысячи таких вот сообщений об ошибках, сбивающих с толку и сложных для восприятия. Hibernate, опять же, если не вдаваться в детали, стремится контролировать значения ID, и, в результате, выбрасывает вышеописанное малопонятное исключение. Учитывая то, что в Spring имеются целые каскады подсистем, основанных друг на друге, стек Spring похож на заклятого врага программиста, который наблюдает за ним и ждёт, когда программист допустит малейшую ошибку, а когда это случается, бросается в него исключениями, не совместимыми с нормальной работой приложения.

Они представляют собой несколько экранов, полных всяких абстрактных методов. Далее, тут же можно вспомнить о длиннейших трассировках стека. Подобный уровень абстракции, несомненно, требует немалого объёма вспомогательной логики, которая направлена на то, чтобы обнаружить всё необходимое для работы кода, например, для того, чтобы выполнять запросы. Spring, очевидно, создаёт конфигурацию, необходимую для реализации того, что выражено в коде. Подобные вещи являются, скорее, симптомом, наводят на вопрос о том, какую нагрузку на систему создают вспомогательные механизмы. И длинные трассировки стека — это не обязательно плохо.

Фреймворку нужно разобрать имя метода, понять намерение программиста, создать нечто вроде абстрактного синтаксического дерева, сгенерировать какой-то SQL-код, и так далее. Как выполняется метод findUserByFirstName, учитывая то, что программист не писал код подобного метода? И всё это существует лишь для того, чтобы программисту не нужно было бы писать код? Как всё это нагружает систему?

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

Мы относились к этому серьёзно, нанося на футболки изображения подобные тому, которое вы можете видеть ниже. Слоган «Compatibility Matters» скрывал в себе замечательную идею, в соответствии с которой важнейшей особенностью платформы Java была обратная совместимость.


Обратная совместимость — это очень важно

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

Java и Node.js

Spring и Java EE чрезмерно усложнены. Платформа Node.js на их фоне воспринимается как глоток свежего воздуха. Первое, на что обращаешь внимание, знакомясь с Node.js — это подход Райана Даля к разработке ядра платформы. Его опыт подсказал ему, что платформы, использующие потоки, нужны для создания сложных, тяжеловесных систем. Он же искал чего-то другого, и потратил пару лет на совершенствование набора базовых механизмов, воплощённых в Node.js. В результате у него получилась легковесная система, которую характеризует один поток выполнения, изобретательное использование анонимных функций JavaScript в роли асинхронных коллбэков, и библиотека времени выполнения, оригинально реализующая асинхронные механизмы. Изначальным посылом при создании такой системы было обеспечение высокой производительности обработки событий с доставкой этих событий в функции обратного вызова.

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

В Java для работы со слушателями нужно создать конкретный экземпляр абстрактного интерфейса. В качестве примера различия между Java и JavaScript рассмотрим реализацию функций-слушателей (наблюдателей). Как разглядеть намерение программиста, скрытое под покровами шаблонного кода? Это влечёт за собой использование громоздких языковых конструкций, которые скрывают суть происходящего.

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

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

Но и оно не лишено проблем. Решение, касающееся использования функций обратного вызова, которое предлагает Node.js, выглядит весьма привлекательно.

Решения проблем и проблемы решений

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

Но это — очередной пример попытки скрытия сложных механизмов которая, ведёт лишь к появлению ещё более сложных конструкций. Для упрощения асинхронной разработки на JS возникли несколько библиотек.

Рассмотрим пример:

const async = require(‘async’);
const fs = require(‘fs’);
const cat = function(filez, fini) ); }); }, function(err) { if (err) fini(err); else fini(); });
};
cat(process.argv.slice(2), function(err) { if (err) console.error(err.stack);
});

Это — невзрачная имитация команды Unix cat. Библиотека async отлично проявляет себя в деле упрощения последовательностей асинхронных вызовов. Однако её использование требует большого объёма шаблонного кода, скрывающего намерение программиста.

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

Если переписать этот код с учётом новых возможностей, которые, в частности, имеются в Node.js 10.x, то получится следующее:

const fs = require(‘fs’).promises;
async function cat(filenmz) { for (var filenm of filenmz) { let data = await fs.readFile(filenm, ‘utf8’); await new Promise((resolve, reject) => { process.stdout.write(data, ‘utf8’, (err) => { if (err) reject(err); else resolve(); }); }); }
}
cat(process.argv.slice(2)).catch(err => { console.error(err.stack); });

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

Единственный недостаток заключается в том, что process.stdout.write не имеет Promise-интерфейса, в результате этот механизм нельзя использовать в async-функциях без оборачивания его в промис.

Вместо этого внесены изменения в язык, что решило и саму проблему, и избавило нас от неудобств, вызванных необходимостью использовать большие объёмы шаблонного кода во временном решении. Теперь можно сделать вывод о том, что проблема ада коллбэков в JavaScript была решена способом, который отличается от попытки скрытия сложных механизмов. Кроме того, с применением механизма async/await код попросту стал красивее.

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

Строгая типизация, интерфейсы и мнимая ясность кода

В те времена, когда я занимался защитой Java от разного рода нападок, я напирал на то, что строгая типизация позволяет писать огромные приложения. В те времена в ходу была разработка монолитных систем (не было микросервисов, не было Docker и тому подобных вещей). Так как Java является языком со строгим контролем типов, компилятор Java помогает программисту избегать множества проблем, не давая ему скомпилировать неправильный код.

Отсюда можно сделать очевидный вывод о том, что программист точно не знает, с какими именно объектами ему приходится работать. JavaScript, в отличие от Java, не отличается строгой типизацией. Откуда программисту узнать, что делать, например, с неким полученным откуда-нибудь объектом?

Программист постоянно производит приведение типов или занимается проверкой того, чтобы всё было в точности так, как ожидается. Обратная сторона строгой типизации Java — это необходимость в постоянном выполнении шаблонных действий. Разработчик тратит время на написание кода, делает это с исключительной точностью, использует немалые объёмы шаблонных конструкций, и надеется на то, что всё это поможет ему сэкономить время за счёт раннего обнаружения и исправления ошибок.

Простого редактора кода тут недостаточно. Проблема программирования на языке со строгой типизацией столь велика, что программист, практически без вариантов, должен использовать большую сложную IDE. Этот и другие вспомогательные механизмы таких IDE, как Eclipse, NetBeans, или IntelliJ, помогают в деле создания классов, облегчает рефакторинг и решение других задач. Единственный способ поддержания Java-программиста в адекватном состоянии (за исключением пиццы) заключается в постоянном показе ему выпадающих списков, содержащих доступные поля объектов или описания параметров методов.

Это — просто кошмарный инструмент. И… не буду говорить о Maven.

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

Относится ли вышеизложенное к плюсам Java или к минусам — зависит от точки зрения.

Сегодня я считаю, что строгая типизация увеличивает объём работы программиста и проекты гораздо легче разрабатывать так, как это делается в JavaScript. Десять лет назад я считал, что все эти сложности оправдывают себя тем, что дают программисту большую уверенность в коде, который он пишет.

Борьба с ошибками при помощи маленьких модулей, которые легко тестировать

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

Вот основные характеристики модуля:

  • Самостоятельность. Модуль объединяет взаимосвязанный код в единую сущность.
  • Чёткие границы. Код внутри модуля защищён от вмешательства в его работу каких-либо внешних механизмов.
  • Явный экспорт. По умолчанию код и данные модуля не экспортируются. Разработчик самостоятельно решает, какие функции и данные нужно сделать общедоступными.
  • Явный импорт. Программист, при разработке модуля, сам решает, от каких модулей он будет зависеть.
  • Потенциальная независимость. Модули можно сделать общедоступными, в весьма широком смысле этого слова, публикуя их в npm, или, если они предназначены для внутренних нужд компании, публикуя в закрытых репозиториях. Это позволяет легко использовать одни и те же модули в разных приложениях.
  • Простота понимания кода. То, что модули имеют небольшие размеры, упрощает чтение и понимание их кода, открывает возможность для свободных дискуссий о них.
  • Облегчение тестирования. Маленький модуль, если он реализован правильно, легко поддаётся модульному тестированию.

Всё это делает Node.js-модули сущностями с чётко очерченными границами, код которых легко писать, читать и тестировать.

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

Ещё одно решение проблемы динамической типизации в JavaScript заключается в тщательном тестировании кода.

Системы тестирования, создаваемые JS-программистом, должны находить те ошибки, которые, разрабатывай он на чём-то вроде Java, мог бы автоматически находить компилятор. Разработчику приходится серьёзно подходить к тестированию, что отнимает у него часть выгод, которые исходят из простоты процесса разработки на JS. Вы ведь пишете тесты для своих JS-приложений?

Я не пользуюсь этим языком, но мне доводилось слышать о нём много хорошего. Тем, кому нужна система статической типизации в JavaScript, может быть полезно взглянуть на TypeScript. Он совместим с JavaScript и расширяет язык системой контроля типов и другими полезными возможностями.

В итоге можно сказать, что использование модульного подхода к разработке — это сильная сторона Node.js и JavaScript.

Управление пакетами

Мне плохо от одной мысли о Maven, поэтому я не могу даже нормально писать о нём. И, насколько я понимаю, Maven, без компромиссов, либо любят, либо ненавидят.

Пакеты Maven существуют, с ними можно нормально работать, их поддерживает Gradle. Проблема тут заключается в том, что в среде Java нет целостной системы для управления пакетами. Но то, как организована работа с ними, и близко не похоже на те удобства, которые даёт разработчику система управления пакетами для Node.js.

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

Зависимости могут быть строгими (скажем, указывается, что нужна исключительно версия 1. Благодаря npm в нашем распоряжении имеется отличная схема для описания зависимостей пакетов. 3 некоего пакета), или заданными с несколькими степенями свободы — вплоть до *, что означает использование самой свежей версии некоего пакета. 2.

При этом использовать пакеты, которых нет в npm, так же легко, как и пакеты из npm. Сообщество Node.js опубликовало в репозитории npm сотни тысяч пакетов.

Раньше там, для управления пакетами, использовались инструменты вроде Bower. Система npm получилась настолько удачной, что пользуются ей не только разработчики серверных продуктов на Node.js, но и программисты, занимающиеся фронтендом. Многие вспомогательные инструменты для клиентской разработки, вроде Vue.js CLI и Webpack, написаны в виде Node.js-приложений. Bower был признан устаревшим, и теперь можно обнаружить, что все JS-библиотеки для разработки фронтенда существуют в виде npm-пакетов.

Основное преимущество yarn перед менеджером пакетов npm заключается в более высокой скорости работы. Ещё одна система управления пакетами для Node.js, yarn, загружает пакеты из репозитория npm и использует такие же конфигурационные файлы.

Репозиторий npm, независимо от того, работают ли с ним с помощью менеджера пакетов npm или c помощью менеджера пакетов yarn, представляет собой мощную основу того, что делает разработку для Node.js такой простой и приятной.

Однажды, после того, как я помогал в разработке java.awt.Robot, я вдохновился на создание этой вот штуки. В то время как официальное изображение Duke состоит из кривых, RoboDuke построен из прямых линий. Только локтевые суставы у этого робота круглые

Производительность

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

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

Название этой виртуальной машины намекает на технику оптимизации, в ходе реализации которой обнаруживается код, который выполняется чаще всего, и к такому коду, чем более интенсивно он используется, применяется всё больше оптимизаций. JDK Sun/Oracle использует HotSpot — виртуальную машину, поддерживающую множество стратегий компиляции байт-кода. HotSpot — это высокооптимизированная система, которая производит очень быстрый код.

Считалось, что на браузерном JavaScript практически невозможно было бы создать нечто вроде набора традиционных офисных приложений. Если говорить о JavaScript, то раньше мы задавались вопросом о том, как можно ожидать от JS-кода, выполняемого в браузере, возможностей, необходимых для реализации каких-либо сложных приложений. Я, например, пишу этот материал в Google Docs, и производительность меня полностью устраивает. Сегодня для доказательства того, что это возможно, далеко ходить не надо. Скорость работы браузерного JS улучшается с каждым годом.

Node.js следует в том же направлении, так как он использует движок V8 браузера Google Chrome.

Здесь он рассказывает о том, почему V8 перешёл с Crankshaft на Turbofan. В качестве примера можно привести это выступление Питера Маршалла, инженера Google, который занимается работой над V8, и основной задачей которого является улучшение производительности этого движка.

Машинное обучение, как и некоторые другие области, нуждаются в средствах для быстрого выполнения численных вычислений. Машинное обучение — это область, в которой используются тяжёлые вычисления, для выполнения которых обычно пользуются языками R или Python. Здесь JavaScript, по разным причинам, не особенно силён, но сейчас ведётся работа по созданию стандартизированной библиотеки для организации численных вычислений на JavaScript.

API этой библиотеки похоже на API TensorFlow для Python, она поддерживает импорт предварительно обученных моделей. Из этого видео можно узнать об использовании в JavaScript новой библиотеки, TensorFlow.js. Эту библиотеку можно использовать, например, для анализа видео с целью распознавания объектов, при этом все необходимые вычисления выполняются в браузере.

Он начинает рассказ с рассмотрения набора бенчмарков, которые демонстрируют значительно более высокую производительность Node.js в сравнении со Spring Boot. Вот выступление Криса Бэйли из IBM, где он затрагивает вопросы производительности и масштабируемости Node.js, в частности, при использовании конфигураций, основанных на Docker/Kubernetes. Более того, от релиза к релизу производительность Node.js серьёзно улучшается, отчасти, благодаря улучшениям, вносимым в V8. Речь идёт о пропускной способности подсистемы ввода-вывода, о времени запуска приложения и о потреблении памяти.

Нам важно понять причину подобной рекомендации. Здесь Бэйли говорит, что Node.js не подходит для выполнения интенсивных вычислений. Я, в моей книге «Node.js Web Development», затрагиваю эту проблему, приводя три подхода к её решению: Из-за того, что в Node.js используется однопоточная модель выполнения кода, длительные вычисления блокируют обработку событий.

  • Рефакторинг алгоритмов — выявление медленных частей алгоритма и его рефакторинг для достижения более высокой скорости работы.
  • Разбиение процесса вычислений на небольшие фрагменты с использованием диспетчера событий, что позволяет Node.js регулярно возвращаться к главному потоку.
  • Передача тяжёлых вычислительных задач на вспомогательный сервер.

Если производительности JavaScript для ваших задач недостаточно, взгляните на следующие два способа интеграции нативного кода в Node.js. Самый простой способ — это использование нативных Node.js-модулей. В наборе вспомогательных инструментов для Node.js-разработки имеется средство node-gyp, которое помогает работать с такими модулями. Вот видео, в котором демонстрируется интеграция Rust-библиотеки с Node.js.

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

Насыщенные интернет-приложения

Насыщенные интернет-приложения (Rich Internet Applications, RIA) были у всех на слуху лет десять назад. Тогда говорили о том, что они, реализованные на базе быстрых (для своего времени) JS-движков, способны сделать неактуальными традиционные настольные приложения.

Sun и Netscape договорились об использовании Java-апплетов в Netscape Navigator. На самом деле, эта история началась более 20 лет назад. Тогда индустрия надеялась на то, что на серверах будут использоваться Java-сервлеты, на клиентах — Java-апплеты. JavaScript был, отчасти, разработан как скриптовый язык для Java-апплетов. Этого не случилось по разным причинам. В результате разработчики попадут в замечательную ситуацию, когда и для серверов и для клиентов можно будет писать на одном и том же языке.

В результате появился модный тогда термин RIA, и ожидалось, что насыщенные интернет-приложения убьют Java в виде платформы для клиентских веб-приложений. Десять лет назад JavaScript оказался достаточно мощным для того, чтобы сложные приложения можно было бы реализовывать исключительно его средствами.

Благодаря Node.js стала реальной та желанная для многих ситуация, когда, и на сервере и на клиенте, используется один и тот же язык. Сегодня мы начинаем видеть, как идея RIA начала приносить плоды. Только теперь это JavaScript.

Вот несколько примеров:

  • Набор приложений Google Docs (использованный при написании этой статьи), который очень похож на типичный комплект офисных приложений, но работающий в браузере.
  • Мощные фреймворки, вроде React, Angular и Vue.js, упрощают разработку браузерных приложений, основанных на HTML, CSS и JavaScript.
  • Electron — это смесь Node.js и браузера Chromium. Данный фреймворк предназначен для разработки кросс-платформенных настольных приложений. С его использованием созданы такие весьма популярные и качественные приложения, как Visual Studio Code, Atom, GitKraken, и Postman.
  • Так как в Electron/NW.js применяется браузерный движок, веб-фреймворки, такие, как React, Angular, и Vue, можно использовать для разработки настольных приложений.

Технология Java, в роли платформы для разработки настольных приложений, умерла не из-за насыщенных интернет-приложений, написанных на JavaScript. Она умерла, преимущественно, из-за невнимания к клиентским технологиям в Sun Microsystems. В центре внимания Sun были корпоративные пользователи, которым нужна высокая производительность серверных приложений. Я видел всё это своими глазами. Настоящим убийцей Java-апплетов стала проблема в безопасности, выявленная несколько лет назад в плагине Java и в Java Web Start. Это привело ко всемирному сокращению использования Java-апплетов и Webstart-приложений.

Однако в этой сфере применения Java наблюдается застой, и за пределами инструментов разработки существует очень немного приложений, основанных на Java. Другие виды настольных приложений всё ещё можно разрабатывать на Java, и, как результат, IDE NetBeans и Eclipse всё ещё борются друг с другом.

Исключением является технология JavaFX.

Планировалось, что эта технология позволит разрабатывать на платформе Java, доступной на мобильных устройствах, приложения, обладающие насыщенным графическим интерфейсом. Технология JavaFX, 10 лет назад, планировалась как ответ Sun на появление iPhone. Но ничего такого не произошло. Это позволило бы одним махом вывести из игры Flash и средства разработки приложений для iOS. В наши дни всеобщее внимание притягивают веб-фреймворки вроде React, Vue.js и им подобных. JavaFX используется до сих пор, но эта технология не видела столь масштабного взлёта, на который рассчитывали её создатели.

В описанной ситуации JavaScript и Node серьёзно обогнали Java.

Такие кольца содержали чип с полной реализацией Java на борту. Вот фотография кольца Java, которое как-то раздавали на конференции JavaONE. Их, в основном, использовали для разблокировки компьютеров, установленных в вестибюле.


Кольцо Java


Инструкция к кольцу

Итоги

В наши дни разработчикам серверных проектом есть из чего выбирать. Индустрия больше не ограничена так называемыми «P-языками» (Perl, PHP, Python) и Java, так как теперь есть платформа Node.js, есть языки Ruby, Haskell, Go, Rust, и многие другие. В результате у серверных программистов теперь есть просто огромный выбор замечательных технологий.

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

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

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

Теги
Показать больше

Похожие статьи

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Кнопка «Наверх»
Закрыть