Телепортировали меня в 2013, тогда как раз был популярен этот компилер и проекты с goog.
Но спираль всё же развернулась, теперь TS силен и много проектов на нем, вот и до Closure Compiler докаталась волна.
Вообще забавно, с одной стороны исходниками, а не каким-нибудь байт-кодом скрипты распространяются в том числе чтобы код видеть, но во благо оптимизации, а иногда обфускации, оно всё же пережимается в нечто среднее, из которого исходный код особо не восстановишь, особенно после этого интструмента, весить становится мало, да, но ощущение что где-то по пути мы обманули самих себя. А потом ещё когда map файлы… в общем спираль крутится.
Так всё просто — есть проблема с одинаковым окружением. Кстати, решение этой проблемы сделало docker таким популярным, например. Тут типа того — разные версии пакетов, иногда и минорно, могут иметь разное поведение. И единственный адекватный кейс — всегда устанавливать именно то окружением что задумывалось разработчиком, иначе это будет порождать пляски с бубном на продакшене и прочее такое. Это плохо, уж лучше пакетов побольше, но всё же стабильная работа.
Ну и вторая причина количества пакетов вообще — любовь к мелкой модульности без таскания целых библиотек. Более того — некоторые популярные библиотеки представляют цельную версию и к ней по модулю на каждую входящую функцию — чтобы разработчик мог взять только одну или несколько, а не тащить с собой 100500 функций ради использования лишь одной. Разве это плохо? Вроде как очень даже хорошо. Ну а то что на npm сайте так много модулей — ну и что, больше выбора наоборот же.
В общем какая-то злая статья без понимания механизмов почему так. Не, конечно некоторые разработчики некоторых пакетов могут лишнее таскать, качество пакетов может быть сомнительным, как и эффективность — но так то это и в любом другом бывает, будь то Ruby или .NET экосистема. А с общим механизмом пакетов всё вполне себе.
Аналогичная проблема была лет 6 назад. Выводилась реклама, в том числе под свежеустановленным Linux, на всех страницах без https, правда с таким нюансом что на первом подключении ничего не было, но со второго и далее — баннеры, при этом судя по всему только на популярных сайтах в свободных от контента местах, ребята заморочились, да, возможно чтобы скрыть факт того что реклама не от реального сервиса. Проблема также оказалась в роутере, что-то туда было дописано извне, в моем случае был просто куплен новый, имелся в этом смысл, баннеры исчезли навсегда.
Возможно у автора аналогичная проблема, хотя всё же похоже больше на вмешательство провайдера.
Тут выше упоминался golos.io и свободу слова. Увы, прилетающие запросы от Роскомнадзора приходится исполнять и запрещённый контент оттуда быстро исчезает… но исчезает из выдачи, оригиналы навечно остаются в блокчейне и доступны через просмотрощик блоков, включая всю историю правок и всё всё всё. Но у голоса было несколько сайтов что выводили контент, был что не скрывал запрещёнку. Закончилось просто баном на территории России.
К слову, вышла новая версия этой штуки, но уже для всего мира, на базе нового блокчейна (но той же линейки, тоже графен). commun.com
Могу что-либо рассказать о мире таких штук как собственно бывший разработчик Голоса.
Но а так, как верно выше заметили — простым людям это не нужно, а для не простых — уже есть десяток проектов на все случаи жизни, но что-то вот доминирующего, который победил бы всех и был на слуху, что-то вот нет. Возможно потому что оно и не нужно. А то «потому что могу» делать всегда хорошо, но лучше сначала понять какую проблему оно решает и для кого, только не взглядом идеалиста, а взглядом прагматика. Но как техническая задача — это весело. Но может лучше присоединиться к существующему проекту?
Очень большое отождествление NodeJS и Express, хотя в названии именно NodeJS. Ну и код в примерах от 2014 года и ранее, так уже не пишут, для веба 5 лет это много, тем более революция с ES6/2015 превратила язык в почти другой.
Очень старые советы, пожалуйста, не применяйте их бездумно.
Или устанавливают правила. Либо поручают машине исполнить договоренность, но чтобы она соответствовала указанным инструкциям. Ну и ещё есть всякие там голосования делегатов за форки. В результате, абстрактно, это взаимодействие и машин и людей в некоторых из квантов времени. Суммарно всё конечно исходит от людей и договариваются люди с людьми, но через посредников-машин. Но это уже философские темы получаются.
Да, это второй вариант развития когда у тебя достаточно средств и власти чтобы преодолеть консенсусный барьер, добраться до состояния когда ты владелец, а после, если твоя цель разрушение или сдерживание — сотворить что-то подобное. Правда вот какая штука — если разрушить, то есть шанс что появятся форки и подобные проекты, а вот если сдерживать, ломать неспешно — могут быть варианты и поинтереснее и поэффективнее.
У ETH:
~11 520M USD торговый оборот за сутки
~27 067M USD капитализация
— 400+ известных рынков
— Большинство бирж торгуют
Итого в 15 раз больше оборотов, в 31 раз больше капитализация, почти все биржы торгуют ETH и много, но не все ETC. Также проблемы с командой и цена токена ETH в 33 раза больше ETC.
То есть это да, не смерть форка, но разница на порядок.
У обычных людей не появилась, потому что как раз таки это и не нужно, есть другие инструменты. А вот у специфических и для специфических кейсов — да. В статье есть пример с Венесуэлой.
Например вот так.
Не то чтобы божественный пример, но рабочий код, без вырезок, переживший несколько доработок и крутящийся на продакшене.
Можно было бы конечно что-то более стремящееся к треугольности в плане колбеков, но либо не безопасно, либо не ищется с ходу.
Отмечу что использую классовый движок ExtJS, но он никак не выпрямляет код, лишь помогает в ООП.
Если вот этот класс написать в треугольном стиле — прокрутка в бок была бы весьма велика, как и разброс кода и мест определения локальных переменных. Не говоря уже о путанице в тех что видны через скоп.
По поводу B.util.Function.queue — утилита просто вызывает по очереди функции, передавая скоп текущего класса и первым параметром отправляя ссылку на следующую функцию, в которой заранее проставлен скоп.
Про обработку ошибок — суть в том что каждый метод сам лучше знает а что у него не так, что поломалось, что необходимо выбросить в исключение, залогировать и т.п. и он сам вызывает метод с ошибкой или сразу отправляет что-то клиенту. Это избавляет от вложенных поимок, катчей уровнем ниже и прочих дебрей вглубь. В итоге — всё плоско и предсказуемо.
Впрочем, перфекционист найдет что здесь ещё можно было улучшить. Например вынести в отдельный субкласс «управляемый режим запуска», можно было бы ещё больше разложить и подоптимизировать код. Но это — полноценный продакшн, не вылизанный код и даже в таком виде он лучше пирамидок, треугольников и цепочек отлова ошибок.
В добавок к этому разделение на шаги позволяет проименовать шаги, что ведет к большему пониманию что происходит в каждый момент времени. Особенно полезно если над проектом работает не 1 человек, и просто необходимо если планируется что код будет поддерживаться годами.
Ну и ещё одна особенность, уже касательно организации асинхронных путей исполнения кода — в рамках одного класса/модуля/etc. должен быть 1 путь. Если в процессе исполнения шагов порождаются вложенные наборы шагов или ветвления — необходимо выносить это в отдельный класс/модуль/etc. Всё это позволит избавиться от непредсказуемости, треугольности и проблем с дебагом.
И всё это на ES3/ES5, то есть промисы добро и только дополняют решение, но в голом виде не решают проблем. А вот архитектура и организация кода — наше всё!
Пример
/**
* Логика размещения компании клиента.
*/
Ext.define('B.biz.client.Release', {
extend: 'B.AbstractRequestHandler',
requires: [
'B.biz.auth.util.Account'
],
config: {
/**
* @cfg {Boolean} isDirectMode
* В управляемом режиме релиз производится логину, указанному в {@link #directLogin}.
*/
isDirectMode: false,
/**
* @cfg {Boolean} directCallback
* В управляемом режиме вызывает эту функцию в момент завершения, вместо отправки клиенту данных.
*/
directCallback: Ext.emptyFn,
/**
* @cfg {Boolean} directCallbackScope
* Скоуп выполнения {@link #directCallback}.
*/
directCallbackScope: null,
/**
* @cfg {Boolean} directErrorCallback
* В управляемом режиме вызывает эту функцию в случае ошибки, вместо отправки клиенту данных.
*/
directErrorCallback: Ext.emptyFn,
/**
* @cfg {Boolean} directErrorCallbackScope
* Скоуп выполнения {@link #directErrorCallback}.
*/
directErrorCallbackScope: null,
/**
* @cfg {String/Null} directLogin
* Логин, по которому необходимо произвести релиз в управляемом режиме.
*/
directLogin: null,
/**
* @cfg {Boolean} isSendSuccessIfPayDateIsExpired
* Флаг, указывающий на то что необходимо отправить что всё прошло успешно
* в случае если истек переиод оплаты,
* не смотря на то что из-за этого релиз произведен не был.
* Может быть использовано в кейсе когда аккаунт был создан только что,
* данные вносятся клиентом, но оплата ещё не произошла.
*/
isSendSuccessIfPayDateIsExpired: false,
/**
* @private
* @cfg {Object} accountData Данные аккаунта.
*/
accountData: null,
/**
* @private
* @cfg {String[]} tagsData Массив данных для тегов.
*/
tagsData: null,
/**
* @private
* @cfg {String[]} tags Массив тегов.
*/
tags: null,
/**
* @private
* @cfg {Object} searchObject Объект поиска.
*/
searchObject: null
},
constructor: function () {
this.callParent(arguments);
B.util.Function.queue([
this.extractAccountStep,
this.validateAccountStep,
this.extractTagsDataStep,
this.makeTagsStep,
this.makeSearchObjectStep,
this.writeSearchObjectStep,
this.sendSuccess
], this);
},
/**
* @protected
* Модифицированная версия, не пытается отправить клиенту ошибку при управляемом запуске.
* Вместо этого вызывает {@link #directCallback}.
*/
sendSuccess: function () {
if (this.getIsDirectMode()) {
this.getDirectCallback().apply(this.getDirectCallbackScope(), arguments);
} else {
this.callParent(arguments);
}
},
/**
* @protected
* Модифицированная версия, не пытается отправить клиенту ошибку при управляемом запуске.
* Вместо этого вызывает {@link #directErrorCallback}.
*/
sendError: function () {
if (this.getIsDirectMode()) {
this.getDirectErrorCallback().apply(this.getDirectErrorCallbackScope(), arguments);
} else {
this.callParent(arguments);
}
},
privates: {
/**
* @private
* @param {Function} next Следующий шаг.
*/
extractAccountStep: function (next) {
var key = null;
var login = null;
if (this.getIsDirectMode()) {
login = this.getDirectLogin();
} else {
key = this.getRequestModel().get('key');
}
Ext.create('B.biz.auth.util.Account', {
key: key,
login: login,
type: 'company',
scope: this,
callback: function (acc) {
var data = acc.getPrivateAccountData();
if (data) {
this.setAccountData(data);
next();
} else {
this.sendError('Данные указанного аккаунта не найдены!');
}
}
});
},
/**
* @private
* @param {Function} next Следующий шаг.
*/
validateAccountStep: function (next) {
var data = this.getAccountData();
var basic = Ext.create('B.biz.client.model.BasicData');
var summary = Ext.create('B.biz.client.model.Summary');
var photo = Ext.create('B.biz.client.model.Photo');
var words = Ext.create('B.biz.client.model.Words');
if (this.isPayDateExpired()) {
this.handlePayDateExpired();
return;
}
data.key = true; // Модели требуют наличия ключа сессии.
basic.set(data);
summary.set(data);
photo.set(data);
words.set(data);
if (!basic.isValid()) {
this.sendError('Основные данные о компании ещё не заполнены.');
return;
}
if (!summary.isValid()) {
this.sendError('Описание компании ещё не заполнено.');
return;
}
if (!photo.isValid()) {
this.sendError('Фотографии ещё не заполнены.');
return;
}
if (!words.isValid()) {
this.sendError('Ключевые слова ещё не заполнены.');
return;
}
next();
},
/**
* @private
*/
handlePayDateExpired: function () {
var successIfExpired = this.getIsSendSuccessIfPayDateIsExpired();
if (successIfExpired) {
this.sendSuccess();
} else {
this.sendError('Невозможно выполнить действие - услуга ещё не оплачена.');
}
},
/**
* @private
* @return {Boolean} Закончился ли оплаченый период.
*/
isPayDateExpired: function () {
return this.getAccountData().payDate < new Date();
},
/**
* @private
* @param {Function} next Следующий шаг.
*/
extractTagsDataStep: function (next) {
var data = this.getAccountData();
this.setTagsData([
data.name || '',
data.word1 || '',
data.word2 || '',
data.word3 || '',
data.word4 || '',
data.word5 || '',
data.word6 || '',
data.word7 || '',
data.word8 || '',
data.word9 || '',
data.word10 || '',
data.address || '',
data.summary || ''
]);
next();
},
/**
* @private
* @param {Function} next Следующий шаг.
*/
makeTagsStep: function (next) {
Ext.create('B.biz.search.util.Tokens', {
value: this.getTagsData(),
scope: this,
callback: function (self, value) {
this.setTags(value);
next();
}
});
},
/**
* @private
* @param {Function} next Следующий шаг.
*/
makeSearchObjectStep: function (next) {
var data = this.getAccountData();
this.setSearchObject({
company: B.Mongo.makeId(data._id),
rating: 0,
tags: this.getTags(),
map: data.map,
payDate: data.payDate
});
next();
},
/**
* @private
* @param {Function} next Следующий шаг.
*/
writeSearchObjectStep: function (next) {
var searchObject = this.getSearchObject();
B.Mongo.getCollection('search').update(
{
company: searchObject.company
},
searchObject,
{
upsert: true
},
function (error) {
if (error) {
this.sendError(B.Mongo.requestErrorText);
} else {
next();
}
}.bind(this)
);
}
}
});
Почему-то все кто пишут о минусах NodeJS всегда пишут о том что получается лапша из колбеков. Но… почему бы не писать эту лапшу? И почему считается что лапша решается использованием промисов? Ведь можно просто… делить шаги на отдельные функции/методы. А не пихать всё в один супер-метод с десятком колбеков, будь то просто функции или использование промисов. Думаю большинству тех кто пишет о таких проблемах необходимо срочно прочитать книгу Чистый код. И хоть речь там идет о Java — эта книга быстро вылечит от проблем с какими-то там вложенными колбеками, промисами и всем всем всем.
Пишу код на NodeJS и не имею проблем с колбек хэлом. При этом не использую ни промисы, ни генераторы, на обычном ES5 всё выглядит как аккуратные классы с методами, в которых нет пирамидок из колбеков и лапши из промисов. И всё работает. И это при логике, в которой по 5 различных обращений в БД за раз, множество колбеков и всего такого асинхронного. И код выглядит хорошо!
Ну и если эта книга, полностью прочитанная и осознанная, не спасет от колбек хэлла — возможно стоит задуматься о правильности выбранной архитектуры?
По сути то софт для государства пишется в том числе за счет налогов с граждан. В идеале во всех странах такой софт должен быть открытым и бесплатным, за исключением того что связано с безопасностью. Хороший вопрос конечно про софт, который был куплен у коммерческих компаний, но тем не менее — это шаг в будущее.
P.S. Боже, как же я стар, 10 лет прошло…
Но спираль всё же развернулась, теперь TS силен и много проектов на нем, вот и до Closure Compiler докаталась волна.
Вообще забавно, с одной стороны исходниками, а не каким-нибудь байт-кодом скрипты распространяются в том числе чтобы код видеть, но во благо оптимизации, а иногда обфускации, оно всё же пережимается в нечто среднее, из которого исходный код особо не восстановишь, особенно после этого интструмента, весить становится мало, да, но ощущение что где-то по пути мы обманули самих себя. А потом ещё когда map файлы… в общем спираль крутится.
Так всё просто — есть проблема с одинаковым окружением. Кстати, решение этой проблемы сделало docker таким популярным, например. Тут типа того — разные версии пакетов, иногда и минорно, могут иметь разное поведение. И единственный адекватный кейс — всегда устанавливать именно то окружением что задумывалось разработчиком, иначе это будет порождать пляски с бубном на продакшене и прочее такое. Это плохо, уж лучше пакетов побольше, но всё же стабильная работа.
Ну и вторая причина количества пакетов вообще — любовь к мелкой модульности без таскания целых библиотек. Более того — некоторые популярные библиотеки представляют цельную версию и к ней по модулю на каждую входящую функцию — чтобы разработчик мог взять только одну или несколько, а не тащить с собой 100500 функций ради использования лишь одной. Разве это плохо? Вроде как очень даже хорошо. Ну а то что на npm сайте так много модулей — ну и что, больше выбора наоборот же.
В общем какая-то злая статья без понимания механизмов почему так. Не, конечно некоторые разработчики некоторых пакетов могут лишнее таскать, качество пакетов может быть сомнительным, как и эффективность — но так то это и в любом другом бывает, будь то Ruby или .NET экосистема. А с общим механизмом пакетов всё вполне себе.
Возможно у автора аналогичная проблема, хотя всё же похоже больше на вмешательство провайдера.
А в NodeJS удобно использовать этот сайт — https://node.green/
Как только LTS версия ноды делает шаг вперёд — самое время проверять чего добавили и изучать если что было пропущено.
BigInt уже там давно и лично продакшн код с ним писал, а вот например штуки с опциональным чейнингом посвежее.
А ещё можно наблюдать как в язык завозят приватные свойства и, судя по всему, скоро завезут и приватные методы.
Вообще там есть чего почитать и будущее прикинуть, один из мной любимых сайтов по фичам и развитию языка уже многие годы.
Тут выше упоминался golos.io и свободу слова. Увы, прилетающие запросы от Роскомнадзора приходится исполнять и запрещённый контент оттуда быстро исчезает… но исчезает из выдачи, оригиналы навечно остаются в блокчейне и доступны через просмотрощик блоков, включая всю историю правок и всё всё всё. Но у голоса было несколько сайтов что выводили контент, был что не скрывал запрещёнку. Закончилось просто баном на территории России.
К слову, вышла новая версия этой штуки, но уже для всего мира, на базе нового блокчейна (но той же линейки, тоже графен). commun.com
Могу что-либо рассказать о мире таких штук как собственно бывший разработчик Голоса.
Но а так, как верно выше заметили — простым людям это не нужно, а для не простых — уже есть десяток проектов на все случаи жизни, но что-то вот доминирующего, который победил бы всех и был на слуху, что-то вот нет. Возможно потому что оно и не нужно. А то «потому что могу» делать всегда хорошо, но лучше сначала понять какую проблему оно решает и для кого, только не взглядом идеалиста, а взглядом прагматика. Но как техническая задача — это весело. Но может лучше присоединиться к существующему проекту?
Очень большое отождествление NodeJS и Express, хотя в названии именно NodeJS. Ну и код в примерах от 2014 года и ранее, так уже не пишут, для веба 5 лет это много, тем более революция с ES6/2015 превратила язык в почти другой.
Очень старые советы, пожалуйста, не применяйте их бездумно.
~11 520M USD торговый оборот за сутки
~27 067M USD капитализация
— 400+ известных рынков
— Большинство бирж торгуют
Итого в 15 раз больше оборотов, в 31 раз больше капитализация, почти все биржы торгуют ETH и много, но не все ETC. Также проблемы с командой и цена токена ETH в 33 раза больше ETC.
То есть это да, не смерть форка, но разница на порядок.
У обычных людей не появилась, потому что как раз таки это и не нужно, есть другие инструменты. А вот у специфических и для специфических кейсов — да. В статье есть пример с Венесуэлой.
Спасибо за развернутый ответ.
Не то чтобы божественный пример, но рабочий код, без вырезок, переживший несколько доработок и крутящийся на продакшене.
Можно было бы конечно что-то более стремящееся к треугольности в плане колбеков, но либо не безопасно, либо не ищется с ходу.
Отмечу что использую классовый движок ExtJS, но он никак не выпрямляет код, лишь помогает в ООП.
Если вот этот класс написать в треугольном стиле — прокрутка в бок была бы весьма велика, как и разброс кода и мест определения локальных переменных. Не говоря уже о путанице в тех что видны через скоп.
По поводу B.util.Function.queue — утилита просто вызывает по очереди функции, передавая скоп текущего класса и первым параметром отправляя ссылку на следующую функцию, в которой заранее проставлен скоп.
Про обработку ошибок — суть в том что каждый метод сам лучше знает а что у него не так, что поломалось, что необходимо выбросить в исключение, залогировать и т.п. и он сам вызывает метод с ошибкой или сразу отправляет что-то клиенту. Это избавляет от вложенных поимок, катчей уровнем ниже и прочих дебрей вглубь. В итоге — всё плоско и предсказуемо.
Впрочем, перфекционист найдет что здесь ещё можно было улучшить. Например вынести в отдельный субкласс «управляемый режим запуска», можно было бы ещё больше разложить и подоптимизировать код. Но это — полноценный продакшн, не вылизанный код и даже в таком виде он лучше пирамидок, треугольников и цепочек отлова ошибок.
В добавок к этому разделение на шаги позволяет проименовать шаги, что ведет к большему пониманию что происходит в каждый момент времени. Особенно полезно если над проектом работает не 1 человек, и просто необходимо если планируется что код будет поддерживаться годами.
Ну и ещё одна особенность, уже касательно организации асинхронных путей исполнения кода — в рамках одного класса/модуля/etc. должен быть 1 путь. Если в процессе исполнения шагов порождаются вложенные наборы шагов или ветвления — необходимо выносить это в отдельный класс/модуль/etc. Всё это позволит избавиться от непредсказуемости, треугольности и проблем с дебагом.
И всё это на ES3/ES5, то есть промисы добро и только дополняют решение, но в голом виде не решают проблем. А вот архитектура и организация кода — наше всё!
Пишу код на NodeJS и не имею проблем с колбек хэлом. При этом не использую ни промисы, ни генераторы, на обычном ES5 всё выглядит как аккуратные классы с методами, в которых нет пирамидок из колбеков и лапши из промисов. И всё работает. И это при логике, в которой по 5 различных обращений в БД за раз, множество колбеков и всего такого асинхронного. И код выглядит хорошо!
Ну и если эта книга, полностью прочитанная и осознанная, не спасет от колбек хэлла — возможно стоит задуматься о правильности выбранной архитектуры?