Pull to refresh

Comments 37

Но для построчного чтения не нужен велосипед. Ведь уже есть Readline

У меня одного ощущение, что то, что приходит на смену аду коллбеков выглядит еще страшнее? Всего этого можно было избежать если бы язык проектировали не на коленке.
Язык проектировали не на коленке, просто проектировали его совсем для других задач. То, что его пытаются использовать в качестве языка общего назначения, не проблема JavaScript.
Что именно вас пугает? Если вы про оператор |>, то он появился задолго до js. И люди, пишущие на F# и Elixir, например, не испытывают никакого отвращения
неа. синтаксис async/await очень красивый и удобный.
генераторы вобще прелесть.

тут пример немного неудачный
У меня одного ощущение, что ада коллбеков не существует. Что это понятие возникло в умах не очень квалифицированной толпы «программистов». И из-за них теперь JS со всех сторон засыпают сахаром. Но ведь внутри то всё тоже самое — всё те же коллбеки, всё тот же event loop в одном потоке.
И если бывалые JS-ники видят в сахаре уменьшение количества нажатий клавиш, то новички не увидят из-за него всю суть происходящего.
А потом мы опять все будем удивляться тормознутости современных web-приложений.

А что вы предлагаете? Вам нужен GoScript? В угоду новичков надо перестать усложнять язык? Но ведь этот язык нынче используется для построения больших и сложных систем. А там нужен сахар, иначе начнут появляться Kotlin-ы, Scala-ы, CoffeeScript-ы и пр… Ещё года 2-3 назад немалое количество "js"-кода было написано на "кофе", и это не спроста. Теперь же вопрос стоит только в: нужна строгая типизация (Flow, TS) или нет (JS).

Я не против нововведений! Я против позиции «раньше был ад коллбеков, а сейчас наступит рай async/await'ов»

А что не так с async/await-ми? Мне в голову приходят только две вещи:


  • они скрывают сложность внутри себя, и новичок может, не разобравшись, нагородить фигни
  • await нельзя прописать вне async, стало быть нельзя просто взять готовый синхронный код и сделать асинхронным

При этом:


  • 1-ый пункт касается любых усложнений, селяви.
  • 2-ой пункт это неизбежность (если отбросить fiber-ы, но там тоже есть обёртки)

При этом код с async-ми в большинстве случаев многократно понятнее и приятнее, чем ручная работа с promise-ми. А матрёшки и прочие структуры из callback-ов так вообще кошмар из прошлого. Я успел повозиться со всеми 3 подходами и async-await это как глоток свежего воздуха.

С async/await всё отлично. Но ведь и с коллбеками не плохо.
И знать надо и то и другое, чтоб не «нагородить фигни».
А сейчас преподносится так: async/await — это круто, а коллбеки — фу-фу-фу.
И знать надо и то и другое, чтоб не «нагородить фигни».

Тут спору нет


А сейчас преподносится так: async/await — это круто, а коллбеки — фу-фу-фу.

Почти всегда это так. Вы ведь про старые (err, result) => {} callback-и? Они буквально всегда фу-фу-фу. Это очень неудобный подход в организации кода, в деле обработки ошибок, в совместимости с современным async-стеком и даже банальной читаемости. Это исключительный случай, когда callback-и выглядят в async-онном коде уместнее, чем promise-ы и async-функции.


Если не согласны — приведите, пожалуйста, пример.


Если же вы подразумеваете callback-и в .then в промисах, то на них никто бочку и не гонит. Это то же самое, что и async-await, и используется повсеместно.

Суть callback hell в двух вещах:


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


  2. Осложнена обработка ошибок. try/catch не работают, приходится полагаться на конвенцию по передаче ошибки в аргументах функции. Здесь очень просто что-то может пойти не так, в особенности в сочетании с первым пунктом.

Решением были промисы. Они компонуются с помощью методов и инкапсулируют обработку ошибок, даже try/catch опять работают.


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


for-await-of ещё интереснее, потому что это новый протокол итерирования. Вот есть в JS синхронный протокол. Это удобно, потому что всё что итерируется, можно использовать в for или вот так [...iter]. Но тут проблема в том, что информация о конце итерирования должна передаваться синхронно. Поэтому нужен новый протокол.


Для простого итерирования по промисам он не обязателен. А вот работа со стримами или генераторами раскрывает его потенциал. Гораздо проще (и надёжнее) пройти по потоку циклом, чем ловить события data, error и end отдельно. Кстати, обработка ошибок в стриме в статье даже не затронута — опять таки, легко ошибиться, если делать вручную, а в случае со стандартными асинхронными конструкциями ошибка не потеряется, а всплывёт к месту вызова.


Так что ад реален, и о спасении души подумать следует :D

UFO just landed and posted this here

Исправил с использованием модуля string_decoder.

UFO just landed and posted this here

<irony>Надо было ещё содержимое printStream в main затолкать. Показать этой глупой хипстоте, что один метод может решать сразу множество задач! И не нужóн нам ваш этот интернетне нужны нам никакие генераторы, пайпы и пр. говнокод</irony>

UFO just landed and posted this here
Но это уже совсем непонятно для таких как вы

Куда уж нам, болезным. Мы почему-то считаем, что строчки экономить ― затея изначально гиблая. И разделение ответственности придумано не на ровном месте.


Впрочем и код выше (ваш, где 1 async) вполне приемлем. Но очень уж чудно выглядит ваше негодование и все эти ярлыки про говнокодеров, "придумать проблему" и пр.). Особенно на фоне сообщения от BerkutEagle-а выше с его "не очень квалифицированной толпы «программистов»".


Раздробление задачи на много мелких (даже однострочных) функций — нормальное явление. А если писать в "функциональном стиле", то там такое вообще на каждом шагу.

UFO just landed and posted this here
async function main() {
    const subproc = spawn('ls');

    await subproc.stdout
    |> toString
    |> chunksToLines
    |> trim
    |> print;

    console.log('DONE');
}

Я бы сравнил ваш код с вот этим ^. А не с цельным. И тогда картинка вырисовывается не в пользу монолита. Тот монолит хоть и краткий, но чтобы понять, что точно он делает надо вчитываться и вдумываться. Эти 4 |> строки выше же элементарны и они говорящие. Есть ненулевая вероятность того, что они будут вынесены в отдельную library/helper и их реализация вообще не будет мозолить глаза. Скажем если приложение много работает со стримами, то запросто.


Вы же когда пишете previous.slice(i + 1) не нервничаете по поводу того, что slice это 3-10 строк кода. Так и тут. Когда я вижу код выше (с '|>') я не вижу всю эту незначащую ерунду со slice-ми, индексами, конкатенациями, циклами. Я вижу следующее:


  • берём stream stdout
  • читаем его содержимое и приводим к строчному виду
  • обрезаем строки
  • выводим на экран

Это вполне себе JS-way. Так очень даже пишут на динамических языках высокого уровня. Сразу ясна суть. Без мишуры.


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

UFO just landed and posted this here
как и у многих других местных минусаторов

Я ни одного минуса никому не поставил в этом топике.

А зачем вы trim убрали?


string.trim() убирает висячие пробелы с начала и конца строки. Без него программа будет печатать другой результат, не такой как в оригинале.

Можно и без функций, переменных, циклов, можно вообще в байткоде сразу писать, че уж там. Чай без сахара пьем, так еще и в JS этой гадости не хватало!
(жаль, только что «говнокод» читается на раз, а эту портянку «ИДЕАЛЬНЕЙШЕГО КОДА» еще парсить энное количество времени придется)
((еще бы let и const научиться юзать по назначению, вообще хорошо бы было))
UFO just landed and posted this here

"О хоспади", опрос на тостере. Докатились. Да ещё и такой кривой. Gentlee неужели вы до сих пор не понимаете, что сравниваете разные вещи?

Да я тут оставлю, думаю никто против не будет.
— Тестируемость
— Удобочитаемость с тайпингами (если будет jsDoc, то можно сразу сушить весла, с TS/Flow — будет просто каша словесная)
— Для того, чтобы понять что происходит, нужно посмотреть только в main(), а не шариться по всему циклу в поисках правды сути
— Повышенная модульность (я легко могу заменить один из элементов логики на любой другой в любой момент времени, без потерь нервов)
— Разделение ответственности (в принципе, это суммирует перечисленные до бенефиты)
— Возможность быстро перейти к телу метода (почти каждая IDE сейчас умеет это, сильно ускоряет работу, при этом не мозоля глаза лишним кодом, который не нужен в данный момент)

Да, второй код В КАКОМ ТО СМЫСЛЕ многословнее, однако нет необходимости спускаться на все уровни абстракций. Можно заглянуть в самый верхний метод и понять, что происходит, буквально за 10 строк. Мы сводим к минимуму словестность самого верхнего метода, его легко читать.

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

Я понимаю, что на проектах, где бизнес логику в коллбеках к кнопкам шлепают — это выглядит несуразно, но такой код обычная практика для больших проектов.
Я вот, возможно, сейчас ошибаюсь, но, кажется, в исходном примере проход в цикле по одним и тем же данным выполняется 4 раза (в каждом из 4-х методов цепочки по разу), а в примере Gentlee — один раз. Я не исключаю, что я не правильно понял как работают асинхронные генераторы выше, но вроде все так.
UFO just landed and posted this here
Проверил, все таки я был не прав. Но я не пользуюсь генераторами, так что это нормально, что я с ходу не правильно понял. Тем не менее, сообщение выше я писал в Вашу защиту.
Алсо, есть мнение, что хипстеры без импортов нынче не живут (на самом деле, даже «деды» уже без них не живут), поэтому обозреваемый код можно одним движением ловко превратить в:
import {toString, chunksToLines, trim, print} from './mySupaHelperLib';

async function main() {
    const subproc = spawn('ls');

    await subproc.stdout
    |> toString
    |> chunksToLines
    |> trim
    |> print;

    console.log('DONE');
}


faiwer — уже упомянул об этом, однако я забыл к своим аргументам добавить этот пункт, соре за даблпост :D

Добавлю свои пару копеек. Монолитный код, который вы, Gentlee, написали, будет сложнее поддерживать. Представьте как вы будете вносить в код такие изменения, например:


  1. Фильтровать и не показывать строки, содержащие слово "test".
  2. Добавить номера строк в вывод
  3. Разбивать строку на несколько, если ее длина превышает некоторый максимум

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

А в Node.js 10 уже можно написать обработчик Веб-запроса — который обращается, например, в MongoDB и возвращает как минимум статус всегда? Или до сих пор надо писать отдельные обработчики ошибок для соединения, открытия БД, поиска и т.п., т.е. на каждый чих — а если что-то забыть, то никакого Response вообще не вернется?

Пардоньте, вы спрашиваете превратился ли nodejs в web-framework? Нет, конечно. Но всё перечисленное вами задача для всяких express.js, koa и пр., а не для самой платформы.

Ребята, подскажите что это за IDE или Color scheme на самое первой картинке?
Да похоже на VS Code с перенесенным сайдбаром влево, кастомной темой для подсветки синтаксиса и без панели табов. Вот как у меня выглядит с темой One Dark Pro:
Sign up to leave a comment.

Articles