Pull to refresh

Comments 56

У меня такое ощущение, что callback hell — это специальный, восьмой круг. Специально для тех, кто «статику пыхой отдает».
Возможно. Однако у меня не было на тот момент времени писать еще и обвязку типа реги/логина, логики доступа к комнатам и их рендера на js'e. Так что я просто взял ранее написанные пыхорешения, встали как влитые.

Сейчас-то все, само собой, на одной платформе работает.
Я второй год на Scala Days продвигаю тему, что разработчики Play — бакланы :) В прошлый раз задавал им вопросы и было много лулзов. Spray куда лучше, хотя скорее не для сайтиков, а для http. Хотя шаблонизатор туда очень просто влезает и с WS не думаю, что какие-то проблемы. Правда чуваки используют scalaz и IDEA иногда не очень понимает, что происходит :)
Ну хз, сейчас мне фуллстек фв как-то приятнее.

Конечно, можно взять http(s) сервис, натянуть шаблонизатор и любимую либу к бд, собрать свой лего. Но опять же — деплой с нуля руками пиши, sbt ковыряй. Выполнимо, не суперсложно, но зачем?

Кстати, плей переезжает/переехал на spray, если мне не изменяет память.
Ну, как бы, api first.

Вот, чтобы клиентский api был удобен, без магии. Да и вроде в Typesafe Reactive Platform для работы с БД Slick положен, а тут по дефолту не он, что странно.

Переехали на spray-io, клиентский api опять же свой.

Может правда я покастомнее люблю, но мы с пацанами когда выбирали, как-то сразу всем spray больше понравился.
Я могу взять и сделать в одну строку из десятка Future один единственный и ждать его исполнения.

Также как и в JS'ике.

Вообще можно было с самого начала написать с оглядкой на грядущий «callback hell».
Или же сразу посмотреть в сторону bacon.js, Rx.

Если ещё есть время — можно поиграться с websockets в эрланге, думаю будет интересно ;)
Да я с Iteratee итак навеселился вдоволь. Ребята вдруг решили, что кто-то по wsу будет гонять тонны мегабайт, поэтому СРОЧНО нужно делать асинхронные итераторы/энумераторы с возможностью вытягивать ограниченное количество данных. Благо, от стандартных on/push не отказались до конца.

Вот теперь хочу перенести WS соединения на акторы в новой версии фреймворка.

Если честно, приелось писать одно и то же ради писанины :) Хочу уже запустить корабль в плавание.
ОФФТОП: пейстбин для совместного кодинга полно, я лично юзаю collabedit.com/
Я когда в 2013 тыкался по поисковикам с запросами типа collaborative editor, в топе были редакторы из статьи википедии. И ничего, что было нужно мне
Может это все ваши энергетики, но подача мне понравилась, легко читать, когда постоянно что-то эмоциональное в тексте происходит
Я с ними завязал и стараюсь более не употреблять, а то вот с животными разговариваешь.

Но на добром слове спасибо.
Присоединяюсь к предыдущему оратору. Стиль изложения понравился.
От callback hell так же отлично спасают промисы.
Для колбэков мне придется писать обертку, которая либо будет считать количество окончившихся функций, либо станет полноценным Deferred/Promise. Да, да, я знаю про существование подобных библиотек для JavaScript. Но это же просто раздутые обертки к тем самым колбэкам.

Я к тому, что нормальных нативных решений на тот момент не было. Не знаю, как там сейчас промисы в ECMA поживают.
А все подобные либы и решения — обвязки, костыли, пусть иногда и красивые, но все же в парадигму языка вливаются не на 100%.
А что вы называете «парадигмой языка»?
А теперь, если вы с полученными знаниями перепишете все заново на JS, используя аналогичные инструменты и библиотеки — получится ничуть не хуже.

1) Хорошая архитектура на языке X всегда лучше плохой на языке Y.
2) Scala + Play vs. raw JS? Что мешало использовать JS-фреймворки?
3) Любая success-story вида «переписал с языка X на язык Y» неинтересна: второй подход к тому же снаряду всегда получится лучше, ибо набиты шишки, и четко известно, что надо писать.
Написал бы я сейчас на JavaScript лучше, чем тогда? Несомненно. Это, возможно, даже выглядело и работало бы попроще. Опыт в решении задач одного типа — это опыт, его не пропьешь.

Однако есть еще одно но, я его в статье не упомянул, так как ну ооочень субъективно. Я получаю удовольствие от Scala, с ее статической типизацией внутри спокойнее как-то что ли. От JS я страдаю, поэтому даже нынешний фронтэнд писал на coffee + angular
Я вовсе не защищаю JS как язык. Сравнивать тщательно спроектированную Scala с написанным в спешке за 10 дней JS — глупо, да и потроллить nodejs-хипстеров я сам в первых рядах. Я всего лишь о некорректности сравнения. Да, то, что есть в Scala из коробки, в JS достигается дополнительными инструментами, но это же не имеет никакого значения для достижения результата.

Angular — это прекрасно; когда приходится дописывать старые проекты, где он не используется, испытываю адские мучения. Ну вот в ангуляре же вы наверняка используете deferred/promise, и никакого callback-hell нет и в помине? А q.all — тот же future, вид сбоку. :) Да, не так эстетично, но работает.

Типизацию можно получить в compile-time как минимум двумя способами — closure compiler и typescript.

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

Ведь кто-то может подумать, что надо срочно выбрасывать написанное на node.js и переписывать все на Scala. Нет, может быть, что-то и стоит выбросить, но причиной тому всегда будет не язык, а неудачная архитектура.
Так да, я говорил о том, что не переписал прототип.

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

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

А ведь могли сразу взять Питон и наслаждаться ;-)
Дык промисы жеж github.com/kriskowal/q — вот кстати дефолтная реализация.

*** Понаписывают Js под энергетиком а потом всякая нечисть является )) ***
Мне промисы помогли, наверно, потому что я начал сразу с них, избежал колбэков ;)
И да, я тоже написал свой GoogleDocs+pastebin+возможность запускать.
промисы, asyncawait для ноды (как простое решение) или fp/frp (как чуть более хитрое) запросто избавляют от многих проблем.
особенно fp/frp, если его правильно использовать.
понравилось изложение, спасибо)
Господа, но колбэки не компонуются! Это гребаный приговор.


Браво!
Node исповедует (в лице npm) концепцию маленького ядра и кучи мелких модулей.

Так-то рекомендовал бы q, например.

Да и вообще, есть еще счастье в виде node --harmony (http://kangax.github.io/compat-table/es6/).

Или еще большее счастье в виде habrahabr.ru/post/116124/

UFO just landed and posted this here
возможно, и ничем. мне привычнее :)
Автор не справился со сложностью и сделал ошибки, затем переписал. Причем здесь Node.js или Scala, мог бы ведь быть ровно обратный порядок. Или любые другие языки. Проблема то в том, что видимо не была изначально продумана архитектура приложения.
Если нравятся акторы, но не нравятся iteratee — пишите на эрланге и будет вам счастье.
Счастья на Эрланге не будет! Проверял. Эрланг счастье толко для тех, кто любит тратить свою жизнь на изобретение велосипедов.
Ибо библиотек нету.
Увы, видимо scala -реальный выбор.
Вы неправильно понимаете, как его использовать.

Эрланг — это роутинг, парсинг и поддержание коннектов. Все остальное — если его мало, тоже на э-ге; если много — на другом языке.
Звучит как-то похоже на «статику генерить и отдавать пыхой» :)

Ну т.е. в каком-то жестком хайлоаде это может и имеет смысл, иначе — еще один промежуточный уровень чего-то, что может поломаться. Мерфи почти всегда, сука, прав.
А каких вам лично библиотек не хватило для счастья?
Ерланг сильно специфичный. Причем в своих специфичных нишах далеко не лучший.
Вот говорят что ерланг хорош для веба.
Лично мне не хватило нормального http клиента, например ;))
Такого, чтобы не умирал на некоторых dns. Такого чтобы >200 коннектов поддерживал ;))
А то как-то нелепо, когда язык позиционирует себя как «многопоточный для интернета»
а over 200 http-коннекшенов- не добится.
И надо самому http клиента пилить.

Я писал на эрланге паука dirs.info/spider
и стриминг сервер к-й стримит поток jpeg'ов через webcoкeты.

В итоге парсил html на недо-парсере от mochiweb'a. Он слабый. XPath имплементированы на половину только. Пилить его и пилить.
Работы со строками нормальной сильно не хватило ;)) Списки-строки идут на юг. Никогда больше!
Когда html страничка была больше 2 мегабайт строки на списках плакали и работали часами. Приходилось со страничками как с бинарниками работать.

Огорчает когда язык позиционируется как кластеризуемый из коробки, и при этом нет zero-deployment'a из коробки.
Меня gridgain на java куда больше радовал в плане легкости работы именно из-за zero-деплоймента.

В общем, считаю что зря потерял время. Лучше бы scala какую выучил или акку.
Говорят сейчас какая-то либа появилась для zero -deploymenta на ерланге. Ну дай-то бог.

Ну про надежность эрланга не буду говорить, это отдельная тема вызывающая холивары.
Ваша история очень похода на мою — я тоже на Erlang на работе пишу пауков уже пару лет (Одного сложного дорабатываю постоянно и штуки 4 помельче). И отчасти я с вами согласен.

* HTTP клиент lhttpc работает отлично, ни разу проблем не возникало. Использовал 500-800 параллельных потоков в штатном режиме. Особо приятный момент — возможность жёстко ограничить время на скачивание странички — не уложился в 20 секунд — отбой. Но поддержку кукисов пришлось дописать. Помимо lhttpc есть вполне годные hackney и ibrowse.

* Для парсинга HTML тоже использую mochiweb_html. Даже бенчмарк делал — работает достаточно шустро, быстрее чем парсеры на большинстве динамических языков, по потреблению памяти так вообще почти всех делает. Зачем вы использовали list — строки вместо binary не знаю, видимо по неопытности…

* Для XPath брал тот же mochiweb_xpath — тут действительно была сильно урезанная версия и пришлось некоторые вещи дописывать — их сейчас влили в апстрим, так что поддержка XPath сейчас практически полная.

Насчёт zezro-deployment — не совсем понимаю что под этим подразумевается, не могу прокомментировать.
Насчёт надёжности не понял какие могут быть вопросы.

Что я получил в замен на свои «мучения»:
* Приемлемую производительность (первоначальная версия паука на Python + gevent + mongoDB для общих данных выдавала в ~10 раз меньше запросов и хрен поймёшь в каком месте был затык)
* Возможность подпатчить код без остановки паука (зашел на сервер, сделал nano src/cookie_storage.erl; rebar compile; erl -remsh spider@localhost; l(cookie_storage). и вот уже паук работает на подправленной версии).
* Удалённый шелл сам по себе отличная штука — можно подключиться к работающему процессу и что-то там посмотреть (включить профилирование / покопаться в памяти / изменить настройки / включить-отключить подсистему). Пользуюсь постоянно.
* Относительно легко подключать другие языки — у меня к пауку подключены интерпретаторы JS и Python.
* Выпилил лишние сущности (в Python версии шаренные данные /списки proxy, статистика, cookies etc/ складывали в MongoDB, т.к. gevent по ядрам CPU не умеет расползаться, в Erlang они хранятся прямо в памяти)
* Сам код стало проще структурировать и поддерживать, т.к. OTP к этому располагает.

С вебсокетами тоже баловался — вот этот сервис написал полностью на Erlang: http://dropmail.me/ru/ и там самые что ни на есть вебсокеты.
зеро-деплоймент когда говоришь выполнить такой-то класс на такой-то ноде
класс (вместе с зависимостями) сам туда деплоится выполняется а и возвращается результат. и все рантайме.
Ну на эрланге видать это бим должен быть ;))

ibrowse — богат на функционал но что-то типа не больше 20 коннекшенов
у остальных функционал беднее.

Хорошо что кто-то платил вам за весь этот допил. вместо того чтобы купить на эти деньги сервак поднять на нем инстансы и гонять паук помедленнее ;))

пауков-то немерянно есть написанных
А при чём здесь язык Питон, изображённый на картинке? :)
Попрошу, это удав!

Отозвался в гугле на безысходность, вот еще цитату вспомнил.
Ммм. Действительно удав. Значит я все эти годы программировал на удаве!
Скажите, вы остались на Mongo? Используете Slick, Reactive Mongo?
reactivemongo и его Future отлично подошли
а можно еще пару слов, что понравилось, что не понравилось в reactivemongo?
Хм.
Понравились неявные преобразования объектов в json и обратно с ходу, ну и неблокирующие запросы офк.
Не особо радует документация на запросах сложнее find(), часто приходится лезть в API и гугл.
Вот так выглядит простенькая агрегация с подсчетом и группировкой:
    val command =
    BSONDocument(
      "aggregate" -> "rooms",
      "$pipeline" -> BSONArray(
        BSONDocument("$match"->BSONDocument("owner._id"->userId)),
        BSONDocument("$group"->BSONDocument("_id"->"$language", "count"-> BSONDocument("$sum"->1)))
      )
    )

    val comm = Aggregate("rooms", Seq(
      Match(BSONDocument("owner._id"->userId)),
      GroupField("language")("count"->SumValue(1))
    ))

    Db.db.command(comm)


Я пока этот запрос родил — прошерстил с десяток страниц апилок и гугл тредов.
Ну это как писать на sql для тех кто никогда не видел в глаза sql, дело просто в том, что тупо не знаешь как этим пользоваться. Вот какая-нибудь обвязка а-ля query builder помогла бы
Да нее. С самим монго я работаю давно и успешно, знаю что к чему.
Просто чтоб это расчехлилось из rm — нужно много стараться.

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

У них же .command сделан как раз для таких вот сложных запросов. Подход простой — нам впадлу описывать это в std api, поэтому сделаем вот так.
Господа, но колбэки не компонуются! Это гребаный приговор.


Второй раз за неделю вижу подобное утверждения. Кто-то может объяснить почему же они не компонуются?
Написать async compose для ноди не так сложно.
колбэки в чистом виде не компонуются. обертки уже упомянуты как и в статье, так и обмусолены в комментах.
Следуя вашей логике, функции в js тоже не компонуются в чистом виде (в js нет оператора для композиции функций). Извиняють сударь, но это смешно.

Я веду к тому, что нодовский коллбеки как раз отлично компонуются. Есть конвенция: коллбек — последний аргумент для асинхронной фунции, ошибка — первый аргумент коллбека. Написать функцию композиции при условии соблюдения конвенции легко, причем можно вариировать семантику обработки ошибок, порядок вычислений.

То что действительно неудобно, так это CPS.
Вот некий JS псевдокод с колбэками, суть которого найдется в любом примере к node.js и mongo

db.getData(filter, function(error, someData) {

});

db.getAnotherData(anotherFilter, function(error, anotherData) {

});


Скомпонуйте результаты выполнения этих двух асинхронных запросов без лишней писанины.

В Scala я сделаю так:
val futureResult = db.getData(filter)
val anotherFutureResult = db.getAnotherData(anotherFilter)

 //Future[MyDataObj]
val completeResult = for(result <- futureResult; anotherResult <- anotherFutureResult) yield MyDataObj(result, anotherResult)

И это будет вполне scalish way, поскольку for скомпилится во flatMap, а это привет монадам, интерфейс которых имплементируется в scala.concurrent.Future. Все смотрится и работает целостно, без всяких конвенций и договоренностей.
В принципе должны уже быть библиотеки, которые оборачивают такие вещи. Если нет — то очень просто написать. И тогда будет:

Q.when([
  Q.nfcall(db.getData, filter),
  Q.nfcall(db.getAnotherData,anotherFilter)
]).done(function(){});
UFO just landed and posted this here
Вот, пожалуйста:

var futureResult = db.getData.bind(db, filter);
var anotherFutureResult = db.getAnotherData.bind(db, anotherFilter);

waitFor(futureResult, anotherFutureResult, function(error, someData, anotherData) {
  // ... do the job
});


Вы правы, стандартная либа js не имеет что то подобного к waitFor. Но то, что такого комбинатора нет в стандартной поставке, еще не значит что коллбеки не могут быть скомпонованы. Нужно всего лиш написать такой комбинатор:

function waitFor() {
  var args = Array.prototype.slice.call(arguments);
  var done = args[args.length - 1];
  var result = new Array(args.length - 1);
  var hasError = false;
  var left = args.length - 1;

  args.slice(-1).forEach(function(asyncFn, i) {
    asyncFn(function(error) {
      if (error) {
        hasError = true;
        return done(error);
      }

      result[i] = Array.prototype.slice.call(arguments, 1);
      left--;

      if (left === 0) {
        return done.apply(null, [null].concat(result));
      }
    });
  });
}


Тот же async — это по сути набор специальних комбинаторов для разных сценариев использования.
Как говорит Дуглас Крокфорд у каждого языка есть свои good and bad parts. На мой взгляд проблема JS в том что он был спроектирован за 10 дней а еще благодаря MS все ее плохие стороны вместо того что бы быть ликвидированными были досконально документированы и вошли в стандарт. А сколько потратил Одерский на Scala? Тем более что автор scala до ее создания трудился над generic в java и имел прекрасное представление о недостатках java. Соглашусь с Дугласом Крокфордом о том что, js это самый широко используемый язык в мире который большинство людей его использующих не понимают. Я люблю оба языка и нахожу что каждый хорош в своей области, но несмотря на это они одинаково хорошо подходят для решения многих схожих задач.
Sign up to leave a comment.

Articles