Как стать автором
Обновить

Комментарии 646

Понимаю, что автор статьи подошел с юмором :)

А теперь по сути, из личного опыта так сказать. Был свидетелем, когда в JS приходили люди из мира Java. Первое, что я слышал — «да ваш JS га… о. Нет типов? Да это убожество! А это еще и работает?». Первое что приходило в голову тогда — «дааа… походу люди какие-то фанатики Java и ничего другого не видят и видеть не хотят».

Т.е люди приходят в JS, считают, что JS это что-то типа для формочек в браузере. Не разбираются, как что работает, не вникают в тонкости и бегут на хабр/стековерфлоу/другой блог и давай писать — JS га… о! Да как на этом можно писать!?! JS девелоперы слепые фанатики!

Читал первую статью, потом вторую и вот третья появилась. Ребят, да оставьте кесарю кесарево. Вам удобно на C#/Java писать? Пишите, вас же никто не заставляет принудительно сменить стек технологий, потому что стильно/модно/молодежно.

PS Пишу на JS уже лет 6-7 и прошел путь от «что-то подкрутить в jQuery» до сложных SPA для ERP/CRM систем, а так же архитектурных решений на NodeJS на серверной части.

PSS пожалуйста, давайте без холивара.

Я просто пытаюсь намекнуть, что фанатизм не выражается в том, что кто-то с упоением пишет на JS. Большая часть статьи пестрит выражениями "ты думаешь" и "ты считаешь". Фанатизм — это больше психологическое. Он проявляется, когда человек начинает на каждом шагу проталкивать идею о том, что "мой язык — хороший, а все кто его не понимает — идиоты". Вот этот самый постфикс про идиотов и отличает фанатика от обычного разработчика. Я пытался вложить посыл "фуфуфу таким быть". :)

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

И ломаются копья, и пишутся посты гнева. Зачем? Что кто кому пытается доказать?

Честно вам скажу, я думал "хм, а ведь в статье JavaScript можно заменить на любой другой язык". Но вот сугубо ИМХО, сугубо из моего опыта — разработчики на JS ведут себя подобным образом чаще. Я знаю Java-разработчиков, я знаю C++-разработчиков. Я даже знаю Haskell-разработчиков. Но самые громкие из них — JS-разработчики. Вот просто так получается по моей, вероятно нерепрезентативной выборке.

Мне кажется, что это из-за того, что JS язык молодой во второй реинкарнации. Я имею ввиду, то, что как нормальный язык он стал только последние 5-6 лет с приходом ES6. И в него влились молодые люди, котороым стоит поумерить самомнение и преобрести терпимости. И я таким был, но ничего, мы взрослеем, набираемся опыта.

Думаю, надо научится уважать все стороны. И если кто-то кичится, то стоит человека на место ставить. Язык программирования тут не причем.

Тем не менее я убежден, что если человеку мягко не намекнуть что он ведет себя некрасиво — он сам не поймет.

Абсолютно с вами согласен. Но еще раз уточню — язык программирования тут не причем.

… о чем и написано в шапке статьи.

Почитал бы такую статью о хаскелистах.

Да там ничего интересного. Просто атрофируется возможность писать на языках без монад и к 8 уровню хочется строжайшей типизации для всего подряд, e.g. хочется иметь тип "циферки от 5 до 10.5"

Чёрт, хочется ведь :(

Ну включите саморефлексию и напишите :) Может мне еще и яичницу за вас приготовить? :)

Я не про статью, а про тип «циферки от 5 до 10.5».

Но это уже ближе ко всяким идрисам. Или ждём ghc 8.8, там обещали допилить зависимые типы.

Рефлексия упрощенно — саомкопание. "Саморефлексия" это уже что-то принципильно новое ;D дедлок какой-то. [/зануда]
Простите не удержался.

Да не, по идее, это идемпотентная операция, так что саморефлексия == рефлексия.

Без вики Вас не понял)) Да все верно, мое занудство ущемлено =)

Пишу (или когда-то писал) на Perl, PHP, JS, Java, C#, C++, Bash, XSLT, Python, ActionScript, Groovy, Lua. Наверняка что-то забыл упомянуть.


Во-первых, все вышеперечисленное дрянь, по тем или иным причинам :) Во-вторых, неперечисленное тоже дрянь.

От каждого по его способностям, каждому — по его труду ©

Пишу на 1С и не парюсь. Зачем все остальные языки?!
В случае 1С — это уже 8 уровень )))

А еще есть те, кто говорит «язык X — говно, но я на нём зарабатываю больше, чем на Y, поэтому на нём и пишу»...

Согласен с Вами, коллега, от холиваров фанатиков нет никакой пользы окружающим…
P.S. Сам я веб разработчик asp .net (C#), недавно дома начал пробовать писать игры на JS, сначала использовал jQuery, потом отказался от него, чтобы лучше изучить JS. Но ни востограюсь никакими языками и принципами, самое главное в нашем деле — простота и изящество решений, имхо…
Вот этот самый постфикс про идиотов и отличает фанатика от обычного разработчика

Ваша агрессивность фанатична

Заметьте, вовсе не js-разработчики всё это на Хабре начали.

Тоесть это адаптация этой статьи на тему JS?)
Пишу на JS уже лет 6-7

Бедненький, такое не лечится.
jQuery — удобный инструмент и искренне тебе помогает в делах

jQuery это устаревший инструмент, имеющий ряд недостатков. Для своего времени это был прорыв, позволяющий писать кроссбраузерный код без уродливых конструкций типа
 var ua = window.navigator.userAgent;
  var msie = ua.indexOf ( "MSIE " );

  if ( msie > 0 )      // If Internet Explorer, return version number
     return parseInt (ua.substring (msie+5, ua.indexOf (".", msie )));
  else                 // If another browser, return 0
     return 0;

да, так писали в 2010 году
Это был один из его плюсов тогда, но сегодня этого всего уже не нужно. Теперь о минусах, с jQuery очень легко писать с нуля, но очень тяжело поддерживать, потому что очень тяжело выявлять связи между кодом js и html разметкой. Как только проект становится больше hello world, очень легко, что то сломать и даже об этом не знать, потому что ошибок в консоли скорей всего не будет.

Слушайте, ей-богу, ваше мнение о jQuery тут никого не интересует. В контексте статьи фраза про jQuery означала "тебе комфортно в jQuery и ты не чувствуешь потребности в чем-то большем". Пожалуйста, смогите в переносный смысл. Я в вас верю.

да нет, мнение интересует кого-то
как раз интересно получить краткое мнение по поводу тезисов, и почему они смешны в этой статье — ведь мне некогда учить полностью весь необходимый стек, я работаю на другом дереве технологий

так что пускай люди пишут свои мнение
и ради бога, не думайте за всех
не пишите «никого не интересует»
Людей который слишком много кричат про «jQuery не нужен» можно довольно легко ловить на простую двуходовочку:
1. Просим их показать аналог jQuery.position()
Они говорят да вот же!!1, смотрите как просто!
2. А теперь нас интересуют координаты элемента, который обернут в элемент с position: absolute, а один из его родителей имеет position: fixed, и еще какой-то из родителей relative и там еще где-то margin'ов понапихано.

Это в общем-то можно всё разрулить, но будет не она строчка, а десятка полтора. И по смыслу не будет отличаться от того, что внутри у jQuery.
А мы вот возьмем и вынесем эту функцию в новую библиотеку — шах и мат.
А потом вторую, третью, пятую, десятую…
Ну а там и до пакетного менеджера недалеко, чтобы управлять ими.

То есть, вы предлагаете, если нужно использовать одну функцию из jQuery, тянуть всю jQuery в проект, если нужна одна функция из lodash, тянуть весь lodash и т.д.?

Ну вот смотрите: C/C++ компиляторы выбрасывают неиспользуемый код при компиляции, и для этого не нужно дробить библиотеки на элементарные. А вот в JS из-за особенностей языка так сделать нельзя, вот и приходится делать надстройки в виде пакетных менеджеров на каждый чих.

НЛО прилетело и опубликовало эту надпись здесь
Если реально одну — нет, но тогда и новая библиотека не нужна.
Однако практика говорит, что одной дело ограничивается очень редко.

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

А еще «use jQuery» это известный мем на stackoverflow. По-моему в контексте статьи смотрится весьма органично. :)
нет никаких трудностей, если для обращений к элементам вы используете только id вместо html разметки и css классов

как только вы фокусируетесь на id элементов, то ваша работа сводится к работе с моделью приложения и javascript + jquery, консоль в таком варианте незаменима

проблемы большого проекта решаются разработкой грамотной модели приложения, а не отказом от jquery
Асинхронные операции в Node.JS для тебя — cutting edge. Ты полностью уверен, что ни в одном другом языке кроме как JavaScript это невозможно и никаким другим фреймворком кроме Node.JS это не поддерживается

Хотя бы в ноде он (async/await) работает так, как ты этого ожидаешь, в отличие например от .net

… и вот мы определили ваш уровень поражения вирусом. :)

Не ставьте диагнозы ;) Пишите больше на js или лучше на ts, который на мой взгляд взял лучшее от js и c#. И тогда вы поймете, что то, что вам казалось разжижением мозга, на самом деле просветление.

Вы видимо шапку статьи не прочитали.

ts, который на мой взгляд взял лучшее от js и c#

Чем ts лучше c#? =) Как по мне, ts хоть и улучшает js значительно, но до c# очень не дотягивает

Э… и в чем же отличие того как работает async/await в js и в .net? :-)

Тот же вопрос: в чём разница? Когда запустил NodeJS — понравилось, что async/await работает также как в C# (даже подумал: близнецы-братья), разве что дедлоков нет по Wait() или Result() — если синхронные операции вызываешь в асинхронном методе.

Ну как же ты не видишь, слепец! В js это работает с магией, а в этом твоём c# работает с энтерпрайзным унынием! Как вообще это можно спутать?!

Смех смехом, а существенная разница всё же есть: в C# для async операции создаётся отдельный поток, а в JS остаётся один поток. Если в JS async функцию поместить какую-либо долгую (не IO) операцию, например вычисления, не заблокирует ли она главный поток: habrahabr.ru/company/jugru/blog/341070/#comment_10499826? Нужно ли её как-то по особенному писать?

В C# асинхронные функции никто в отдельный поток автоматически не выпинывает.

В смысле?

В прямом. Вызов функции, объявленной как async, выполняется без создания дополнительного потока.

При необходимости отдельный поток всё же используется: stackoverflow.com/q/37278281
Но прятать такие штуки внутри асинхронных функций — дурной тон.
Это понятно, в основном конечно используется для ввода-вывода. Но C# потоки всё же использует для этого.
Создание потоков — это особенность не C#, а стандартной библиотеки. Потоки создаются явным образом внутри самих асинхронных функций и/или в планировщике.

Например, мой собственный планировщик асинхронных операций, написанный на C#, является полностью однопоточным.
А по моим тестам последний node.js с турбофаном фсё-таки быстрее: github.com/spion/express-vs-dotnetcore — раза в 2-3 в зависимости от условий.

Ну да, потому что ни Wait, ни Result в js у обещаний нет :-)

Тот же вопрос: в чём разница?

разве что дедлоков нет

Хммм… действительно… в чём же разница… ;-)
разве что дедлоков нет по Wait() или Result() — если синхронные операции вызываешь в асинхронном методе.

Эта проблема решается просто: не используем синхронные операции вообще и используем свой планировщик. Да-да, механизм async-await — это просто синтаксический сахар, что в C# (обёртка над Task<>), что в JS (обёртка над Promise). Вам ничто не мешает в C# здесь так, чтобы абсолютно всё выполнялось в одном потоке, и никакие Wait() и Result() не будут нужны.

Синхронные операции не использовать вообще не получится. Типичный пример — обращение к асинхронной операции в конструкторе. Да, делать тяжелые операции в конструкторе не рекомендуется, но частично-инициализированный объект возвращать хуже. Да, можно написать фабрику, которая будет асинхронно создавать объект, но это не всегда применимо, в частности в случае наследования таких конструкторов.

Но это же не повод делать дедлок...

Всё таки существенная разница есть: в C# для async операции создаётся отдельный поток, а в JS остаётся один поток. Если в JS async функцию поместить какую-либо долгую (не IO) операцию, например вычисления, не заблокирует ли она главный поток: habrahabr.ru/company/jugru/blog/341070/#comment_10499826? Нужно ли её как-то по особенному писать?
Я по причине обучения в ВУЗе столнулся с JS, до этого долго и упорно сидя на Lua. Ну первое время у меня матов не хватало, что бы описать JS, и если бы не jQuery, то возможно и сейчас бы не хватало.

У меня была та же реакция, когда я после JS врубался в Lua.

Слова вместо фигурных скобок не красиво соглашусь. То что функция может вернуть несколько результатов не привычно. Условия циклов и ветвлений можно обернуть в скобки чтобы выглядело привычно. То что всё можно использовать как ключь это круто. Правда пользовался этим только при сериалиации. То что конкатенация это отдельный оператор это плюс. С булевыми операциями всё просто. Всё true кроме false и nil.

«Всё как ключ» в общем удобно при работе с рекурсивными структурами. Не только сериализация, мне для конвертации в другие рекурсивные типы данных пригождалось (вроде из типов lua в C’шные структуры, используемые в другом языке программирования). Булевые операции в таком виде удобны, т.к. иногда позволяют обойтись без тернарного оператора. Но оператор был бы лучше.


Что мне категорически не нравится — это стандартная библиотека. io.popen не умеет даже такое малой части subprocess.Popen из Python как «не использовать shell» и «открыть одновременно на запись и чтение». setenv нет, хотя getenv есть. Конечно, в lua намеренно минимальная стандартная библиотека. Но я специально привёл примеры того, что я считаю присутствующим, но не полностью реализованным функционалом, а не тем, что вообще хотелось бы видеть в стандартной библиотеке.


Второе, что мне не нравится — совместимость. В lua её ломают, поэтому многие просто сидят на 5.1. Luajit до сих пор полностью совместим с 5.1, от 5.2 взято только то, что не нарушает совместимость, 5.3 в changelog вообще не упоминается. В Gentoo всё, кроме 5.1 замаскировано, maintainer’ы потихоньку монстрячат слотовый lua, чтобы ставить несколько версий lua одновременно — как я понял, множество пакетов в новой версии не работает и никто не хочет их чинить, поэтому выбирать не‐5.1 в качестве основной версии просто нельзя.

Нет, больше вымораживало отсутствие массивов.

Таблицы в Lua выполняют так-же роль массивов. В отличии от JS манипуляции с "массивами" выполняются внешними функциями но если очень нужно их можно и прицепить к таблице.


t = {"a", "b", "c"}
t.concat = table.concat
print (t:concat("*")) --> a*b*c

В JS то тоже чистых массивов нет. Это просто тот-же объект с дополнительными фунциями работы с массивом.


Таблица в Lua аналог обьекта в JS.

В PHP вроде тоже. Но это как‐то непривычно — я с Python и C на lua переходил. До 5.0, кстати, никаких оптимизаций для таких таблиц не было, всегда честный хэш (с соответствующей константой для вещей вроде table.insert), с 5.0 там гибрид массива и таблицы. table.insert всё равно, правда, не содержит оптимизаций вроде “если можно, просто запусти memmove()”, но гибридность снижает затраты на lua_rawget()+lua_rawset(). Тем не менее, судя по коду в 5.1 оператор # (длина) всё ещё О(log(N)). В 5.2—5.3 и luajit — не знаю.

http://moonscript.org/. Как CoffeeScript, только для lua.

Как‐то пытались использовать в Neovim, но отказались. Главная проблема: тесты на moonscript проваливались, никто не знал, почему. Другие проблемы:


  1. Если вы сделали ошибку в синтаксисе lua напишет, какая и где была ошибка. Если вы сделали ошибку в синтаксисе moonscript, вы получите «Failed to parse», номер строки и всё.
  2. Если вы сделали ошибку не в синтаксисе, то вы получите ошибку. А номер строки — нет, только номер строки в результирующем lua коде который в общем случае недоступен (вы можете сделать конвертацию локально, но вам нужна та же версия moonscript и, возможно, что‐то из окружения).
  3. Moonscript отсутствует в репозиториях различных дистрибутивов. А у нас не lua пакет, чтобы ставиться из luarocks, где moonscript есть.
  4. Линтеров практически нет. Я знаю только встроенный и moonpick, при чём второй не существовал во время принятия решения, а первый весьма ограничен.
  5. Мажорная версия языка до сих пор 0, что означает, что язык будет считаться нестабильным. (Сам автор сказал, что он не использует semantic versioning и не считает язык нестабильным сам, но это скорее ещё один минус.)
  6. Moonscript добавляет ещё один протекающий слой абстракции.
  7. Людей, которые могут писать на moonscript неизбежно меньше тех, что могут писать на lua, потому что первые — подмножество вторых, потому что без знания lua отлаживать moonscript код нельзя.

Соответствующая дискуссия, окончившаяся тем, что текущий (@tarruda сейчас фактически не участвует в разработке) лидер проекта сказал, что будет использоваться moonscript, хотя больше людей было за lua. Пример неизвестных проблем с moonscript. Другой пример.

У вас ссылка на «JavaScript как праздник» введет туда же, куда и «JavaScript как явление».
однако мой основной профиль — C# и делать всё, чтобы защитить бедных C# разработчиков от излишнего ныряния в JavaScript.
«Бедные» C# разработчики не обязаны нырять с головой в JavaScript, если компания позволяет себе нанять отдельно разработчиков C# и JavaScript. Статья написана конечно же не для этого (по моему впечатлению). По вашей статье складывается впечатление, что все, кто пишет на более новых технологиях, чем JQuery — «больны».
Есть в уровнях (особенно последних двух) некоторые описанные симптомы, которые действительно кажутся маразмом, но не все. Например:
Едешь на конференцию по JavaScript. Там тебе впервые показывают как за 20 секунд сделать сервер на Node.JS. Восторгаешься. Вирус, подпитавшись материалами с конференции откусывает ту часть мозга, которая еще помнила про многопоточность. Она помирает и не успевает задать свои вопросы
Объясните, разве это плохо — ездить на конференции по своему языку программирования? Плохо, что в какой то технологии можно за 20 секунд сделать что-то?
Хочу обратить внимание, что первым появился пост, который обсирает JavaScript, и негодует его резкой популярности, второй же пост просто защищает JavaScript.
«Здоровый» человек не будет обсирать другую технологию, которую он не понимает, которая стала вдруг популярной. «Здоровый» человек напишет про свою любимую технологию. Не хочешь JavaScript на backend, не применяй (если ты решаешь чему быть), убеди тех. директора чем C# лучше JavaScript, не работай в компании с «больными» людьми, и все остальное в этом духе.
Мне кажется больше разводят холивар не JavaScript разработчики, а те, кто не являются JavaScript разработчиками, но кому приходится им пользоваться, ибо он сильно отличается от других.
У вас ссылка на «JavaScript как праздник» введет туда же, куда и «JavaScript как явление».

Fixed


По вашей статье складывается впечатление, что все, кто пишет на более новых технологиях

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


Плохо, что в какой то технологии можно за 20 секунд сделать что-то?

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


«Здоровый» человек не будет обсирать другую технологию

Пост как раз об этом. В шапке же написано, что я критикую фанатиков, а не технологию

Плохо, если технология скрывает от тебя очень важный аспект реализации чего-либо, а ты сам не желаешь разобраться как оно все-таки работает.
Простите, но разве не этим же самым заняты 99,99 любых библиотек на любых языках, да собственно и самих языков высокого уровня? Это же всё направлено на то, чтобы ты не воевал с инструментом за каждый шажок, а занимался решением своей прямой задачи.

Все верно. Однако — опять же — в хороших ВУЗах зачем-то изучают что такое стэк, как передаются параметры в функцию и вообще учат C. Если ты хотя бы ориентировочно понимаешь как и что работает — это помогает идентифицировать и обезвредить любой потенциальный факап, который может возникнуть с технологией. В случае многопоточного программирования — даже в node.js можно поймать race condition. И вот представьте что вы наткнулись на race condition, а вы не знаете что это :)

А что, в C нельзя устроить race condition, не понимая что это такое? Он настолько сложен, что без 5 лет учёбы в ВУЗе на нём нельзя сделать такую тривиальную вещь, как параллельный/асинхронный блок кода?
Да и тем более, race condition он что, спалит вам компьютер и аннигилирует вселенную, если вы за 5 секунд его не нейтрализуете? Для его устранения нужны какие-то сакральные знания, которые нельзя восполнить в пределах 15 минут?

Эаам…
Наоборот — на C очень легко сделать race condition с posix-тредами и натренироваться такие ситуации подмечать и бороться с ними.

Тогда я вообще не понимаю. Вы ругаете язык за то, что в нём сложнее отстрелить себе ногу, потому что отстреливая себе ногу гораздо легче научиться не отстреливать себе ногу?

Послушайте, я не ругаю языки. Я ругаю людей, которые не учатся основам перед тем, как начать делать что-то серьезное.

Да, для устранения race condition нужны знания, которые нельзя восполнить за 15 минут. Очень интересно наблюдать, как такие проблемы решают разработчики с завышенным самомнением — обвешивают костылями, а потом отмазываются — php наверное сбоит. Любые абстракции текут, поэтому фундамент нужно знать.

Я не очень понимаю.
C# / C++ / Haskell / Java / etc -разработчики активно нападают на JS.
Js-разработчики защищают свой язык.


Фанатики как раз первые, а вовсе не вторые. Почему вы написали пост про js-фанатиков, а не haskell-фанатиков или c#-фанатиков?

Я вот фанатиков от этих языков особо как-то не встречал. Сообщества ведут себя абсолютно адекватно, не строят из своих технологий серебрянную пулю и не считают быдлом всех, кто не пишет на %язык_нейм%, не общаются рекламными штампами. Кроме того, относительно адекватно реагируют на вопросы. E.g. каждый раз, когда я задавал вопрос по C++/Java любому из опытных разработчиков на оных — я спокойно получал ответ и разъяснение как и что работает. Каждый раз, когда я задавал вопрос по JS-у, я получал в ответ упреки, что я ничего не понимаю, мне надо учить самые основы и вообще "не умеешь — не лезь". Так один из адептов рассказывал мне снисходительным тоном про прототипное наследование — будто Америку открывает. Делал он это в ответ на вопрос о виртуализации прокрутки. И такие ситуации возникли несколько раз подряд — вот я и подумал что что-то с разработчиками на JS не так. Когда они начали отрицать индустриально-базовые вещи, такие как строгая типизация и многопоточность — я понял что под видом сакрального знания чаще всего подают вопиющую неквалифицированность. Ну я не выдержал и все заверте… Я просто скомпилировал все штампы, которые слышал в одну отдельную статью

Это фанатиков хаскеля-то вы не встречали? Видимо, вы мало с ними общались. Более яростных теоретиков, далёких от бизнеса, я никогда не видел. Фанатики C++, которые всё что выше C++11 называют хипстотой — тоже интересные личности.
C# и Java — встречал мало, да. С этим лучше.

Понимаете в чем дело, ни фанаты haskell, ни фанаты C++ не станут подвергать сомнению такие базовые дисциплины как операционные системы, сетевые технологии, базы данных или теория типов. В отличие от.


Они могут сказать что я пишу на не труъ-языке, но они никогда не скажут что многопоточность — это плохо.

Многопоточность это сложно, а сложно это плохо. Согласитесь, без неё мир был бы проще. Просто есть задачи, которые без многопоточности решаются еще сложнее. И кстати многие среды выполнения JS внутри используют потоки. Взять тот же node.js, не говоря уже о браузерах.

Промышленная химия — это сложно. А сложно это плохо. Согласитесь, без лекарств мир был бы проще.


Просто, как вы заметили, без лекарств людям жить еще хуже :)

Просто, как вы заметили,

Ну что уж так уж?). Самые крутые истории, которые доходили из поддержки, связаны с эффектами, возникавшими в многопоточных приложениях. Не знаю какими таблетками кормят разработчиков, может такими когда приходится выбирать или печень, или почки.

Вы не поверите, но это реально круто, когда код можно тупо дебажить, покрывать юнит-тестами, а потом он реально так и работает.

Как вы покроете юнит-тестами конструкцию "показывать пользователю UI, давать работать с локальной копией БД, в то же время пытаться установить подключение к серверу с увеличивающимися интервалами в 10, 20, 30 сек и далее минута, но отвалиться после 5 попыток"?

Мы сейчас точно говорим про юнит-тест (это которые должны тестировать SUT, абстрагировав от всех внешних зависимостей?) Мне почему-то кажется, что функцию которую вы описали, надо покрывать матом — слишком много на себя берет. :)

Во всех реализациях JS таймер является внешней штуковиной, которая благодаря этому тривиально заменяется на фейк. Например такой http://legacy.sinonjs.org/docs/#clock. Все предельно просто, как в детском конструкторе, и это правда работает.

При чем здесь таймер? Я вам не про функцию говорю, а про целую систему. Вы можете покрыть 2 потока отдельными тестами, но интеграционно вам придется тестировать это вручную.

Можно вспомнить, что многопоточность — это монада, и сделать аналог quickcheck-подобного тестирования для всех возможных последовательных представлений двух потоков (возможно, со снапшотами состояний для каждого из потоков, ибо race condition'ы же тоже надо уметь тестировать).

Извините, я на вашем клингонском не говорю :)

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

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

Ну а что до кэшей и оптимизаций компиляторов — представляется разумным (формально) проверять это отдельно и единожды, а затем рассчитывать на это как на работающий и доказанный примитив.
Прикол в том, что 2 потока отдельными тестами это имитация бурной деятельности. 2 потока в тесте и 20К потоков при эксплуатации могут давать совершенно разную картину для одной и той же сборки. А это, в свою очередь, резонно порождает отдельную пачку вопросов относительно практической полезности и экономической оправданности таких тестов в контексте раннего обнаружения класса багов, специфичного лишь для многопточного кода.

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

Вот вы и сами видите. Фанат haskell не скажет, что многопоточность, это плохо. Он хотя бы про видеокарты вспомнит, там многопоточность — это хорошо. А вы сказали, даже объяснение придумали. В этом разница.

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

Я так на первом году жизни работы с хаскелем думал, но только вот понять, что map fibonacci [0..1000] для наивной рекурсивной реализации Фибоначчи ­— дорогая штука, и её лучше распараллелить, а map (*2) [0..1000] — нет, не очень тривиально.

Но вы правы, в подавляющем большинстве случаев параллелизм достигается заменой map на parMap rdeepseq, а конкурентность — каким-нибудь forkIO в main.

Забавно, кстати, как раз сейчас нашёл баг в ghc, который выкидывает некоторые аннотации параллелизма.
Тогда можно еще задачу комивояжора припомнить (и вообще там целый класс задач из мира «очевидное-невероятное программирование», ни чем не хуже, чем «1 + '2' == '2' + 1» в JS). А про фанатов это конечно под впечатлением шутливого тона статьи. Правда, если воспринимать буквы совсем буквально, то меня можно и канделябром (запрещено законом РФ). Только вместе с автором, ведь не я первый здесь это начал.
Я не очень понимаю, как вы сравниваете задачу коммивояжера и типизацию в JS. Наверное, это всё от слабой типизации.
Сожалею, что пример оказался путанным. Я имел ввиду задачи, где глядя на код, результат не совсем всегда очевиден. В случае map fibonacci и задачи нахождения оптимального пути в графе в хаскеле получается неожиданно плохая асимптотика. По-моему это ни чем не лучше, чем приколы с приведением типов в JS.
Я имел ввиду задачи, где глядя на код, результат не совсем всегда очевиден.

У любой оформленной в виде кода задачи есть intrinsic complexity (этакая неизбежно связанная с ней сложность), сложность, связанная с языком (ну трудно писать на брейнфаке, что поделать) и сложность, связанная с радиусом кривизны рук программиста. Когда результат не совсем очевиден только для задач с большой intrinsic complexity, это хороший, годный язык.

В случае map fibonacci и задачи нахождения оптимального пути в графе в хаскеле получается неожиданно плохая асимптотика.

Смотря как написано fibonacci. Если это каноничная рекурсивная реализация, то вести она себя будет действительно паршиво, но она в любом языке будет вести себя паршиво. Мемоизирующую реализацию написать довольно просто за счёт свойств языка:
fib = (fibs !!)
    where fibs = 1 : 1 : zipWith (+) fibs (tail fibs)


А с оптимальным путём — что здесь не так?

… и эта самая мемоизирующая реализация все еще в связке с map будет работать медленнее чем хотелось бы, хоть и не будет уже так тормозить.

Мы ж про асимптотику, а не про константу ;)

Если вы о том, что!!! — O(n), то возьмите Data.Array вместо обычного списка, и будет примерно то же самое, что и для императивного нечистого языка. Если хотите, чтобы результаты шарились между разными вызовами — ну, вам нужно будет ST или IO уже тогда, глобальный стейт же, и это правильно.

Если бы не было многопоточности, мы бы до сих пор сидели бы на древних компутерах.

Многопоточность — сложно по сравнению с использованием callback?
Многопоточность это сложно, а сложно это плохо

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


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

Многопоточность — это не плохо. И не хорошо. Просто в рассуждениях о node.js, противопоставляя его многопроцессному подходу, смешивают в кучу «людей и коней».

Ведь все пользуются однопоточным nginx для статики, не так ли? Так вот. Всем же очевидны его преимущества? Так вот. Типовая модель использования node.js — та же: «принеси-подай» из базы. Накладных на преобразование http-запроса в sql/mongodb-запрос практически нет (ну не более чем nginx резолвит filename:)). Забрать из базы данные — почти та же статика, которая не содержит логики обработки по запросу (в отличие от типичного рендеринга в PHP). Чтение файла — такая же асинхронная операция, как и выборка.

Стандартный способ деплоя node.js — это pm2, который кластеризует процессы node по ядрам. По сути, мы перещаем бутылочное горлышко рендеринга из серверных процессов на клиента (в современных virtual dom-технологиях и серверах api на node.js по типу loopback). То есть нагрузку на сервер облегчаем, и это честно так.

И кстати, многопоточность — это плохо, когда накладные на переключение начинают влиять. Разница между процессами и потоками с точки зрения ОС небольшая (общая память => общий краш для потоков, для процессов нет), и вряд ли кто поспорит, что 4 процесса по 1000 коннектов лучше 4000 потоков (пусть даже не процессов) в системе. Банальный fork в линухах должен будет скопировать окружение процесса, проинициализировать процесс и прочие приседания на синхронизацию (где-то родительский поток ждет или поллит семафор заверешения чайлда).

Я вот не говорю, еще раз, что многопоточность плохо (или хорошо). Я говорю о том, что рассуждать о ней надо в контексте конкретной системы.
Ведь все пользуются однопоточным nginx для статики, не так ли?
Мир клином не сошелся на IO-bound задачах. Более того, IO-bound задач настолько мало, что их обычно изолируют и забывают (тот же nginx например).

Типовая модель использования node.js — та же: «принеси-подай» из базы
Да-да, в простонародье — формошлепство (а точнее, его бекенд). Почему же тогда фанаты JS начинают брызгать слюной, когда им говорят что их язык не годится для чего-то более серьезного? (ну, или, более толерантно, подходит хуже чем другие промышленные языки).

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

Пример вам под руку: написать GET-метод, который полезет в БД, достанет оттуда данные, соберет из них excel-файл и отправит клиенту. Вот тебе бабушка и IO-bound задачи юрьев день

Самое печальное, что так ведь и делают: крутят в одном потоке как генерацию xls, так и асинхронный чатик. И потом еще не верят что чатик зависает. У нас же NodeJS, это невозможно! Но если что, виноваты конечно же программисты, а не экосистема, которая их воспитала.

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

Ну, проблемы генерации больших excel-файлов на количестве потоков не заканчиваются. Так что тут node.js и C# для меня одинаковы — в обоих случаях я предпочту вынести генерацию файла в отдельный процесс :-)

Генерация-то ладно, она обычно не раздувается по памяти и CPU до бесконечности. А вот парсинг — может, как уже не раз показывал опыт с Apache POI, PDFBox и Tika.

Не скажите. Мне вот в этом году пришлось фиксить багу вида "после выгрузки в Excel справочника на 100000 строк сервер перестает отвечать". Правда, эта ошибка воспроизводилась только в тестовой среде, где веб-сервер и сервер СУБД за память воюют — но в другой процесс генерацию я все равно в итоге вынес.

Я повторюсь. Обработку картинок нейросетью на сервере я не буду писать на node.js. То что называют «формошлепством» — далековато от истины в случае того же loopback, который можно настроить на абсолютно всё.

Я противник лишней работы на сервере, и считаю, что для большинства современных задач (работаю в немаленькой веб-студии), скажем > 80%, node.js более чем достаточно.
что для большинства современных задач (работаю в немаленькой веб-студии)

Наверное, вы хотели сказать "для большинства задач веб-студии".

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


Разумеется, есть еще уйма CPU-bound задач, которые на ноду ложатся мягко говоря не очень. Но это ведь не делает ноду плохим инструментом, для решения io-bound задач.


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

Вот статья, рассказывающая о том, как работает nginx. Суть её — вся магия работает покуда у вас только io-bound операции. А nginx как раз для таких и предназначен (раздача файлов, статический контент). А вот другая, не менее объемная статья о том, как сложно nginx-у жить с cpu-bound операциями и как он вынужден с ними справляться. Повторяю еще раз. Насущная CPU-bound операция для бизнеса, например — генерация документов (тот же самый excel). И покуда остаются проекты с cpu-bound операциями (а почти все серьезные проекты именно таковы) путь для nodejs в мир серьезного продакшена закрыт.

Ну с чего бы закрыт-то? Просто надо в отдельные потоки или процессы такие операции выносить.

А это уже многопоточность. Это уже, как выше было JS-разработчиками сказано, сложно. А сложность, как они же нам объяснили — не нужна :)

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

И через что будет строиться этот обмен сообщениями? Shared memory? Добро пожаловать в мир всего того, что вы сказали. Пайпы? Попрощайтесь с кроссплатформенностью. Сетевые сокеты? Ну вариант, но как по мне — серверное приложение, которое подключается само к себе выглядит как минимум странно.


Плюс к тому же — если я правильно врубаюсь, то nodejs как раз активно пытается уйти от излишних потоков в пользу архитектуры а-ля nginx. Однако во-первых тредпул она уже активно использует, во-вторых… будет еще один? :) Так или иначе разработчикам придется управлять потоками. А они, как сами дают понять — не умеют.

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

Ну фиииг знает. Я слабо представляю себе nodejs-разработчика, который без опыта классического мультитрединга будет строить IPC на сообщениях.

А зачем нужен сложный обмен сообщениями, чтобы построить xls в отдельном потоке? Запустили процесс и ждем ответа. Я бы сделал как-то так:


const exec = require('execa');

app.get('/get-xls', async (req, res) => {
   const params = getParamsFromRequest(req);
   const data = await db.requestData(params);

   const {stdout} = await exec('node', ['./generate-xls', data]);
   res.send(stdout);
});

Как-то странно вы данные через командную строку передали. Они же структурированные и их много. Да и итоговую xls в памяти держать — тоже неправильно. И обработка ошибок нужна...

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


А если не нравится загружать весь xls в память, то можно взять stdout как поток и через pipe завернуть его в response.


Получилось бы больше кода, но не страшно.

В файл её и отдать через sendFile

Я думал об этом варианте, в памяти с данными работать быстрее, чем писать на диск. (Мы же не делаем XLS-таблицы на 8 Гб?)


В любом случае, варианты есть, а утверждение автора pnovikov, что в node.js все плохо с многоточностью/многопроцессностью — неверно

Да варианты-то есть всегда. Просто, например, в C#/MVC генерация xls (cpu-bound) не является проблемой вообще и никаких "вариантов" для неё не надо — берешь и пишешь (с асинками разумеется)

2 гигабайта в последнем поколении сборщик мусора не собирает пока его не пнуть. А после пинка — собирает довольно долго...


Я бы не говорил что в C#/MVC генерация xls не является проблемой вообще.

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

Верно, совсем забыл про stdin. C ним будет намного лучше!

Так message-based IPC в event-driven языке с managed memory и async/await искоробки, это так же просто и прямо как котят гладить.

Это Erlang. Ну или C# с Akka. Но JS-разработчики так не хотят. Они хотят Javascript :)

Пайпы? Попрощайтесь с кроссплатформенностью.

Эмгм. Что у QLocalSocket, что у System.IO.Pipes проблем с кроссплатформенностью не наблюдал. Причём у второго не наблюдал даже во времена Mono 2.6.


Просьба пояснить, что имелось ввиду.

После прочтения статьи я немного удивился: ни в Windows, ни в Linux нет полноценной возможности асинхронно читать файлы.

Постойте, я и не говорю, что нода хороша для CPU-bound операций. Больших фишек у ноды ровно две:


  1. асинхронный io с приятным интерфейсом для простых смертных (нет, последователи Карри, для этого не нужна специальная монада, и да, парни из мира Стандартных Изданий, для этого не нужно упражняться с тредпуллами), который внезапно показывает весьма приятную производительности без лишнего тюнинга.
  2. javascript. Который, внезапно, не смотря на все фу-фу-фу, доступен практически любому разработчику на Java, C, C++, C#, Pascal, PHP итд. А если немного подтянуть матчать и разобраться в том как работает this и прототипное наследование (которое, вообще говоря надмножество Java-like OOP), то внезапно оказывается, что это вполне пристойный инструмент. А если копнуть еще чуть глубже, то внезапно оказывается, что JS можно декорировать типами (flow), проверять линтерами, собирать под платформы разной степени древности (babel) и использовать фичи из черновиков грядущих стандартов прямо вот сейчас (опять, привет babel). Чем, не смотря на некоторую костыльность и соберисамность, может похвастаться далеко не каждый ЯП.

Теперь про CPU-bound и тот-самый excel. Я конечно в офисных xml делах не очень рублю, но что-то мне подсказывает, что если мы собираемся массово генерировать excel- файлики, то я бы предпочёл это действо передавать системе и вызывать отдельным процессом. Потому что наверняка есть хорошая сторонняя/системная тулза, которую не стоит пытаться запихнуть ни в ноду, ни в похапэ, ни в питон.


Это не значит, что надо писать на ноде всё и вся. Но для типовых веб-серверных задач нода — прекрасный, доступный и достаточно мощный инструмент.

Я бы взял какой-нибудь js-xslx и генерил бы файлики на клиенте. Если нужен один файлик на много скачиваний… да, придется делать его на сервере (в таких случаях обычно это редкий запрос). Можно дернуть процесс (опять хоть с тем же js-xslx), хотя в доках по js-xlsx виден метод writeFileAsync (с коллбэком).
Я бы взял какой-нибудь js-xslx и генерил бы файлики на клиенте

Хватит! Люди. Не везде и не у каждого суперкомпьютер. Тестируете своё приложение на своих машинах с 16 гб памяти и core i7. И думаете что всё в порядке. Любой чих перекладываете на клиент, что бы не дай бог сервер не загрустил… Вы там на адруино приложения хостите что ли?! Генерировать файл на клиенте…

А в чем проблема? Eсли речь идет не о 8гб таблиц, то до определенного предела фронтенд вполне себе в состоянии из json получить xml.


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

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

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


Если рендеринг является бутылочным горлышком — распараллельте его. Все равно процессорное время, затрачиваемое БД на обработку запросов, много больше времени рендеринга.


Почему-то Facebook, ВКонтакте до сих пор используют PHP. Можете это пояснить?


И кстати, многопоточность — это плохо, когда накладные на переключение начинают влиять.

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


Кстати, что-то подобное было в Windows 3.1, когда приложения (процессы?) работали в многозадачном режиме, но приложения в явном виде сообщали системе (DoEvents), что они готовы передать управление в другое приложение.

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

Вставлю 5 копеек внеапно, в защиту JS.


рендеринг на стороне клиента — просто попытка оправдания использования новомодных технологий

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


Если рендеринг является бутылочным горлышком — распараллельте его

Рендеринг не параллелится в общем случае. Максимум что можно — сделать его поточным. Чтобы соединить пайплайн отрисовки и записи в сокет. Но и тут все сложно.

НЛО прилетело и опубликовало эту надпись здесь

Думаю что backend на JS — это очень плохая идея. Поэтому изоморфизм в общем случае не взлетит, покуда у нас нет технологии для изоморфного рендеринга не на JS.

НЛО прилетело и опубликовало эту надпись здесь

Простите великодушно, у меня чуть другой взгляд на проблему — я уже почти собрал движок для изоморфного рендеринга на C#. :)

НЛО прилетело и опубликовало эту надпись здесь

Что-то вроде. .cshtml-шаблон с некоторыми специальными расширениями за счет хитрых перегрузок TextReader-а, при запросе выдает не HTML, а специальную рендер-функцию, которую можно через шаблонизатор вызвать на клиенте и она вернет строку с HTML-ем, которая пропускается через поточный HTML-парсер на state-машине и через атрибуты обратной привязки связывается с вью-моделью. Можно делать VDOM-перерисовку (сейчас вот запиливаю, о чем сказал в статье). Тот же .cshtml-шаблон технически можно выполнить и на сервере, получив тот же результат.


Я все хочу выдрать эту фиговину в отдельную библиотеку и присобачить к ней какой-нибудь транслятор из C# в JS, распарсив существующие ts-тайпинги в C#-интерфейсы. Просто чтобы посмотреть что получится.

НЛО прилетело и опубликовало эту надпись здесь

Ну вам же не нужно перепиливать существующие скрипты и компоненты, когда вы пишете на TypeScript. :)


Я эту идею вынашиваю уже давно и по задумке это получается очень похоже на React.js, только не на JS, а на C#. Помимо этого возможна прозрачная интеграция с ASP.NET MVC-бекендом. Т.е. вы можете вот те же интерфейсы REST-контроллеров использовать тут же, в клиентском коде, что сделает, например, swagger идейно бесполезным для таких проектов.

НЛО прилетело и опубликовало эту надпись здесь

Ну там конструкция построена так, что кто рендерит основную часть верстки — не важно. Главное чтобы в ней были атрибуты обратной привязки. На любой HTML-элементы вы можете добавить специальный backbind-атрибут @Callback("initPlugin"), который вызовет абсолютно любую JS-функцию после отрисовки элемента — и этот элемент функция получит первым параметром. Так же на вашу совесть ложиться добавить @DestroyCallback("killPlugin"), который вызовется при удалении элемента из DOM-дерева. Если ваш коллбек добавляет какие-либо дополнительные ноды — то лучше обернуть это место в div с атрибутом @VDomDontTouch(), чтобы VDOM-перерисовка не корежила ваши jQuery-плагины (спасибо bano-notit за эту идею). Это как оно работает сейчас. В случае если использовать транслятор из C# в JS, то аргументы @Callback и @DestroyCallback скорее всего будут переписаны на строго типизированные (лямбда-выражения).

А рассматривали опыт аналогичных предшественников (gwt, например или jsjinja)? Предыдущие попытки пока заканчивались не очень из-за тормозов при выполнении, проблем с безопасностью и с отладкой генерированного кода.

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

Например, первое, что приходит на ум. На бекенде, в обычной среде исполнения ASP.NET MVC, дернуть из chtml метод, выполняющий запрос в базу, можно счесть и за шалость. При передаче такой имплементации на клиента, даже если решены все технические проблемы, это может стать дырой в безопасности. Аналогично получается со всеми сторонними зависимостями, библиотеками и т.п.

Поэтому писать в такой системе все равно придется с постоянной оглядкой на клиент-сайд, т.е. еще на любимом языке, но «как бы на JavaScript». Все бы ни чего, но это на порядок усложняет (удорожает) как задачу самого кодинга, так и последующей поддержки.

Не волнуйтесь, я знаю что делаю :)

А можете рассказать каким образом это решается в вашем фреймворке, и какие накладываются ограничения?

В каком фреймворке, простите? Это пока еще только задумка — у меня на руках только изоморфный шаблонизатор.


Что касается "дернуть метод из cshtml" — в настоящий момент этот метод выполнится на сервере и его результат запишется в клиентский шаблон (да, параметризуемый шаблон для шаблона — так тоже бывает). Методы, которые влияют на генерируемую шаблон-функцию и нативные C#-конструкции строго друг от друга отделены. В случае если у кого-то чешутся руки пошалить — кидается ошибка компиляции.

Спасибо, теперь слежу.

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

Согласен, но из соображений экономии трафика, а не экономии производительности. Конкатенация строк — это быстрое действие.


Чисто концептуально, если её можно перекинуть на клиента, который бьет баклуши

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


Рендеринг не параллелится в общем случае. Максимум что можно — сделать его поточным. Чтобы соединить пайплайн отрисовки и записи в сокет.

Как это? Почему клиенская машина может рендерить страницу независимо от сервера, но сервер этого делать в общем случае не может?


Но и тут все сложно.

Подумайте о старых технологиях, где это было совсем просто.

Мы видимо разные вещи подразумеваем под словом "рендеринг". Я имею в виду формирование HTML для страницы. И в современных условиях, мне кажется, клиент вполне в состоянии сделать это сам, имея нужные данные. Серверу конечно не сложно, но идеологически это не его задача, хотя де-факто много лет он её выполнял. Под тем, что эта задача не параллелится я имею в виду, что невозможно ускорить формирование HTML для страницы с помощью нескольких потоков. Тем более, как показывает лично мой опыт, сформировать большую строку (и даже её распарсить) — это пустяки по сравнению с отрисовкой. И раз уж клиенту все равно её рисовать — то пущай и формирует. Нече сервер по пустякам дергать :) "Вас много, а я одна", "у меня таких как вы тысячи" и все такое.

Мы видимо разные вещи подразумеваем под словом "рендеринг". Я имею в виду формирование HTML для страницы.

Я тоже.


И в современных условиях, мне кажется, клиент вполне в состоянии сделать это сам, имея нужные данные.

Конечно, в состоянии. Но у клиента только браузер с JS. А на сервере HTML может формироваться гораздо более эффективными способами.


Под тем, что эта задача не параллелится я имею в виду, что невозможно ускорить формирование HTML для страницы с помощью нескольких потоков.

Но это и не нужно ускорять. Тем более, что это пустяки по сравнению с отрисовкой.


И раз уж клиенту все равно её рисовать — то пущай и формирует. Нече сервер по пустякам дергать :) "Вас много, а я одна", "у меня таких как вы тысячи" и все такое.

Если клиент получает от сервера HTML с минимумом скриптов — отображение такой страницы для клиента — лёгкая задача.


Если клиент получает от сервера JS-бандл + данные, причём скрипт однократно генерирует HTML при каждой загрузке/обновлении страницы — это тоже нормально. Один раз протормозится, дальше будет хорошо. Огромный минус — тормозить будет при открытии каждой новой вкладки.


Но если скрипт работает на уровне DOM (типа, так удобнее программистам) — клиента ожидает лагалово, особенно, когда данных много.

А на сервере HTML может формироваться гораздо более эффективными способами.

Если в двух словах, то я просто отдаю клиенту JS-функцию, которая принимает на вход модель и возвращает строку с HTML. И эта функция генерируется на сервере (разумеется, результат легко кэшируется), в неё переносятся все эффективные способы формирования HTML. Читайте — шаблон компилируется на сервере. Клиенту остается просто вызывать — и вуаля. HTML есть. Дальше — хоть через innerHTML вставляй.


причём скрипт однократно генерирует HTML

Таки я не понял — мы предположили что получить HTML — это сложно или это просто? :) Клиенту так и так нужно будет преобразовывать полученный HTML в DOM-дерево и если мы предполагаем что шаблон-функция все-таки простая и не завешивает его на 10 секунд (даже мне с моими кривыми руками не удалось добиться такой просадки производительности, хотя я пытался) — то разница в итоге получается небольшая. А бандл, кстати, можно и кэшировать.

Если в двух словах, то я просто отдаю клиенту JS-функцию, которая принимает на вход модель и возвращает строку с HTML.

И это хорошо.


Клиенту так и так нужно будет преобразовывать полученный HTML в DOM-дерево

Разница в том, что браузер HTML в DOM преобразовывает быстро.
А вот формирование DOM напрямую, используя JS — это медленная задача.


А бандл, кстати, можно и кэшировать.

Но браузер все равно будет тратить сколько-то времени просто на его парсинг.


P.S. Я JS-программист уровня 0, и мой взгляд — чисто со стороны пользователя: просто не люблю, когда сайты тормозят.

Аээ… DOM можно сформировать только в браузере. Нельзя в браузер "отдать DOM" — узлы привязаны к контексту и единственный способ их сериализации — HTML (да и то без привязанных событий). Таким образом единственный путь — сделать чтобы HTML появился в браузере любым способом, а дальше создать DOM-узлы или через innerHTML, или через самописный парсер.

Разница в том, что браузер HTML в DOM преобразовывает быстро.
А вот формирование DOM напрямую, используя JS — это медленная задача.

А можно пруф?

Ой, а можно я за него отвечу? :)


На самом деле короче нет. innerHTML чуть быстрее чем document.createElement и они оба даже работают чуть быстрее, чем изначальный парсеринг/рендеринг HTML-документа при загрузке страницы. Источник — личный опыт.

Мой личный опыт говорит обратное. Я даже писал бенчмарки (но не сохранил). Потому и прошу пруф.

Там сильно зависит от браузера на самом деле. Через innerHTML можно отрисовать в общем случае не все, поэтому треба использовать только document.createElement (в общем случае). Например в IE7 вы не можете проставить innerHTML у tbody (внезапно). Поэтому только через парсер и createElement. И да — оно работает СУЩЕСТВЕННО медленнее чем в IE9 и Edge. В хроме вроде проблем с производительностью не возникает. Но с таблицами тоже не все работает.

Еще есть метод insertAdjacentHTML, он вроде как быстрее установки свойства innerHTML работает.

А можно пруф?

Ну вот, например. Правда, там используется jQuery. Если использовать VanillaJS, то результаты будут другие: в старых браузерах генерация HTML была более быстрым вариантом, в современных — всё наоборот. Видимо, я не был в курсе последних оптимизаций браузеров.

Тут еще смотрите какой момент. Если вы выведете на страницу, скажем, 70 тысяч элементов (я выводил 10 тысяч строк в таблице по 7 колонок, без доп. форматирования), то у вас все будет тормозить вне зависимости от скорости генерации HTML и создания dom-узлов. Ну просто потому что браузеру сложно отрисовать такой объем данных. Ну и как правило, пользователям видеть столько данных одновременно — тоже не с руки. Там уже вступают в игру всякие виртуализации, пейджинги и прочее разделение интерфейса на части. Так что практической пользы в создании огромного числа элементов нет — они все равно не смогут отрисоваться как надо. А небольшое число элементов в любом случае инстанциируется довольно быстро. Так что мерить производительность document.createElement тут не совсем релевантно.

Ну зачем сразу 10 тысяч? Сколько-то там лет назад при выводе совсем простой таблички я наблюдал подтормаживание уже при 50 строчках с использованием JQuery, без же него нормально было только до 300-400 строчек.

Межпрочим, мой Lattice и был основан сначала на jQuery. Потом, когда я познал быстродействие на 100 строчках — мне пришлось сделать свой HTML-парсер

По вашей первой ссылке сравниваются разные способы использования innerHTML, а не работа со строковым HTML против DOM.

Ведь все пользуются однопоточным nginx для статики, не так ли? Так вот. Всем же очевидны его преимущества?

https://nginx.ru/en/docs/ngx_core_module.html#worker_processes:


worker_processes number | auto

https://nginx.ru/en/docs/ngx_core_module.html#thread_pool:


thread_pool name threads=number [max_queue=number];

Как минимум в linux процессы и потоки технически отличаются не столь сильно.

Так ведь и в ноде есть cluster. Слово "однопоточный" в данном случае указывает не на количество потоков, а на модель работы с сетью, когда сокеты распределяются по существующим потокам вместо создания нового потока на каждый сокет.

Т. е. если я использую thread pool, скажем, в jetty, то программа/среда выполнения внезапно стала однопоточной о_О?

На Луркморе упомянуты и каталагизированны_усе :)
Это фанатиков хаскеля-то вы не встречали? Видимо, вы мало с ними общались. Более яростных теоретиков, далёких от бизнеса, я никогда не видел.

Почему? Bus factor высокий, да, но кроме этого в чём проблема?

Фанатики C++, которые всё что выше C++11 называют хипстотой — тоже интересные личности.

Это какие-то неправильные фанатики, которые скорее держатся за свои знания 15-летней давности и не хотят учить новое.

Проблема в том, что человек может часами рассуждать и спорить о теоретической чистоте, вместо того чтобы решать задачу (это я, если что, о фанатиках, которые прямо-таки упарываются, а не вообще обо всех; такие личности есть не только в хаскеле, но почему-то среди них видел намного больше, наверное заумность языка способствует).


неправильные фанатики

А что, бывают фанатики правильные? :) Вот тот пример про C++ (тоже в холиваре про JS кстати). И ещё знаю много таких любителей старого брутального C++, которые даже auto не используют пока по голове не настучишь.

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

Я таких больше всего среди обчитавшихся паттернов видел :(

А что, бывают фанатики правильные? :)

Ну, те тоже фанатики, просто статуса-кво или своего ощущения крутости от разработки на худших технологиях 80-х, а не свежего языка.

Я вот правильный фанатик, мне вчера кусок С++20 понадобился.

Вот тот пример про C++ (тоже в холиваре про JS кстати). И ещё знаю много таких любителей старого брутального C++, которые даже auto не используют пока по голове не настучишь.

Знаю-знаю таких. В соседнем треде как раз вялотекущий срачик где-то с марта идёт.

Первая статья из этого цикла "JavaScript как..." была написана как раз-таки функциональщиком, отрицающим такие индустриально-базовые вещи, как return и скобочки.

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

"Так что же вы стоите, батенька?! Давайте к нам, на бrоневичок, на бrоневичок!"

И глаза такие добрые-добрые..

А ведь мог и TypeScript-ом полоснуть.

Всегда приятно, когда люди опознают классику ,)

Пост как раз об этом. В шапке же написано, что я критикую фанатиков, а не технологию


Так нужно было и писать о фанатиках, а не о технологии.
Объясните, разве это плохо — ездить на конференции по своему языку программирования? Плохо, что в какой то технологии можно за 20 секунд сделать что-то?

Нет, ездить на конфернцию не плохо.

Плохо терять критическое мышление в результате этой конференции)))
именно об этом пишет автор.

в который раз раз повторюсь: я был свидетелем безумия рубистов и пхпшников, которые запихивали свои «супертехнологии» в промышленные проекты ( со сложной бизнес-логикой, высокими требованиями к надежности, и большим объемом обратываемых в одном запросе данных, с высоким темпом изменений, в условиях большого объема кода и сложной логики) — не понимая где лежит граница применимости их технологий.
и я видел, что происходило с этими проектами через полгода, через год, через 2. ничего хорошего.

ведь, имхо, именно о потере критического мышления пишет автор во фразе «вирус откусывает ту часть мозга, которая помнит о многопоточности — она умирает не успев задать свои вопросы».

событийно ориентированный стиль разработки прекрасен.
но строго до тех пор, пока им не пытаются замень что то сугубо многопоточное или требующее синхронности.
скажите, вы когда либо писали код взаимодействия с промышленым принтером через rs-232? а я писал.
и в гробу я видел, извините за выражение, тех, кто написал «событийно ориентированные» классы, которыми я был вынужден пользоваться. потому что когда ты работаешь с принтером — тебе надо поддерживать его состояние и контекст, а проверять это всё каждый раз когда ты просыпаешься в «onByteRecieved()» — настолько усложныет код, что мне пришлось отдельно сидеть и писать «синхронные обертки» над асинхронным интерфейсом работы с ком-портом. потому что только синхронный интерфейс позволял мне сохранить простоту структуры кода и прозрачность логики. а обертка работала, только благодаря многопоточности.

вот такая сказка об идеализации однопоточности и событийности в реалиях жизни.

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

«Здоровый» человек не будет обсирать другую технологию, которую он не понимает, которая стала вдруг популярной. «Здоровый» человек напишет про свою любимую технологию.
Не хочешь JavaScript на backend, не применяй (если ты решаешь чему быть), убеди тех. директора чем C# лучше JavaScript, не работай в компании с «больными» людьми, и все остальное в этом духе.

несколько не правы, имхо. действительно здоровый человек знает ограничения и границы прменимости технологии. и использует js там где он пригоден, а java там, где java работает лучше всего, с# — там где ему место, а не убеждает тех директора что c#/java лучше или хуже js.

у каждой технологии — свои границы применимости. и, напрмер, имхо, пихать js-на сервер, это прмерно так же, как java-апплеты встраивать на страницы публичного сайта. в принципе, работает, но «здоровые» люди так как правило не делают, потому что слишком много минусов, и ограничений у получившегося решения.
событийно ориентированный стиль разработки прекрасен.

Ну, к слову, async/await в JS позволяют весьма неплохо причесать код, избавив от необходимость делать цепочку колбэков, замыканий и хранений состояний.


Касательно работы с ком-портами в C# — да, это недоразумение. Я предпочитал работать напрямую через WinAPI.

Работал с COM-портами в шарпе, никаких проблем не видел. Ну, там конечно довольно старый АПИ на EAP, вместо тасок, но в целом довольно адекватно.

так, народ)))
во первых… не в шарпах я работал. дело было в году 2006-м 2007 и сишарп тогда был… далеко не самым интересным, чем можно было заниматься. от слова совсем.

во вторых — я делал на с++ кроссплатформенную обертку над работой с компортами, и в виндовой части проекта я работал с WinAPI и использовал неблокирующее чтение с коллбеком. Потому что реализовать блокирующее чтение с таймаутом — … ну вот как-то не вышло. В линуховой части все вышло на ура, а вот c WinAPI — матов было много. А таймаут надо было иметь.

а потом туда же (через этот интерфейс) был пристыкован доступ к «виртуальному компорту» ( serial профиль USB-устройства — некоторые принтеры делают именно так)… и матов было примерно столько же.

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

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

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

нет))) не правильнее))
не будет а-синхронный интерфейс давать вам видеть нормально логику работы кода и отслеждивать диалог с принтером.

Мыслите не уровнем «stateless» механизма принял-отдел, а уровнем всей задачи, когда вам надо хранить состояние и (очень желательно) контекст исполнения.

Вот давайте по порядку на примере

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

Рассмотрим на примере псевдо c++ кода гипотетической функции инициализации принтеров определенной серии с использованием синхронных функций:

Функция waitForBytes() — синхронная. не отдает управление пока не получен минимум 2 байта, или не истек таймаут. Если получено больше чем ожидается — отдает весь полученный объем данных.

int initPrinter()
{
serial->send( {0x??, 0x??, 0x??}); //запросим модель принтера
byte[] resp=serial->waitForBytes( 18, 5000); //ждем ответ принтера - минимум 8 байт, 5 секунд.
if (resp==NULL) return -100; // timeout
if (sizeof resp <8 ) return -101; // ответ не распознан
String model = new String(resp);
if ( ! model->startsFrom("Posiflex") ) return -102; // не поддерживаемая модель принтера
if ( ! model->equals("Posiflex PP5200") ) 
  {
     serial->send( {0x??, 0x??, 0x??}); //запросим прошивку принтера
     resp=serial->waitForBytes( 2, 5000); //ждем ответ принтера - 2 байта, 5 секунд.
     if (resp==NULL) return -100; // timeout 
     ...
     if (resp[0]==0x23 and resp[0]==0x24) // упс... у нас тут модель с прошивкой с известным багом
      {     
          serial->send( {0x??, 0x??, 0x??}); //уст. кодировку 866 - в этой прошивке исходно 1251
          resp=serial->waitForBytes( 2, 5000); //ждем ответ принтера - 2 байта, 5 секунд.
          if (resp==NULL) return -100; // timeout 
           ....
     }
  };
serial->send( {0x??, 0x??, 0x??}); //переведем принтер в страничный режим с абс. позиционированием
resp=serial->waitForBytes( 2, 5000); //ждем ответ принтера - 2 байта, 5 секунд.
if (resp==NULL) return -100; // timeout 
  ....
return 0;
}

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

Поймите, тут как раз тот случай, когда асинхронная логика «ну не в вперласть» никаким боком.
От слова совсем.
Удобно и просто прозрачно когда один поток, который ведет диалог с принтером, и ждет ответ от него, с сохранением контекста исполнения и состояния. Потому что это позволяет просто и легко описывать сложную логику диалога с устройством.

Если вам сложно мыслить принтером и последовательным компортом, то представьте, что у вас сокет на удаленный хост — у вас кастомный протокол, и в процессе «рукопожатия» — вам надо запросить у него кучу подробнотей, и в зависимости от того, что он вам ответит — что то ему рассказать дополнительно, или не рассказать.

В языках, которые умеют async/await (тот же C#), в вашем примере все сведется к такой замене:


serial->waitForBytes( 18, 5000)

на


await serial->waitForBytesAsync(18, 5000)

В результате код ничуть не усложнился, а взаимодействие стало асинхронным. Так что асинхронные интерфейсы при наличии соответствующих механизмов ничуть не сложнее синхронных.

Скажите, а вы точно понимаете что такое асинхронный и синхронный вызов?

Вы использовали выражение await, для того, что бы явно сделать синхронный вызов функции, которая заявлена как «асинронная», и говорите что взаимодействие стало асинхронным? вы уверены что не запутались?

Синхронный вызов — это когда исполнение приостанавливается в точке вызова.
Именно это вы и делаете, когда предлагаете использовать await.

А блокируется или приостанавливается при этом поток, какие потоки при этом вызываются, что используется внутри или не вызываются, что происходит внутри — это уже технические детали рализации.

Например похожие технические эффекты, (похожие на await) можно добиться в Java 'без всяких await' с использвоанием yeld() — он отдает ресурсы потока на другие задачи, не не разрушая контекст исполнения.

А «асинхронный» вызов, не приостанавливает выполнение кода в точке вызова в ожидании ответа, а сразу преходит к следующей инструкции.
Вы использовали выражение await, для того, что бы явно сделать синхронный вызов функции.
Синхронный вызов — это когда исполнение приостанавливается в точке вызова.
Именно это вы и делаете, когда предлагаете использовать await.

Синхронный — это если бы я написал waitForBytesAsync.Wait().
Здесь же, если говорить грубо, await приводит не к синхронному ожиданию, а просто код после него будет вызван, когда асинхронная операция завершится. В остальное же время поток будет свободен и ничего ждать не будет.

https://en.wikipedia.org/wiki/Asynchrony_(computer_programming)


await — оператор синхронизации места его использования в зависимой задаче с завершением асинхронно запущенной задачи. В сумме получается вполне себе синхронное исполнение. То, что блокируется не системная "нить", а легковесное "волокно", сути не меняет.

То, что блокируется не системная "нить", а легковесное "волокно", сути не меняет

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

Системные нити точно так же "выходят", а потом "возвращаются". Единственное, что отличает explicit реализацию сопрограмм (которой нужны async/await) от implicit (которой они не нужны) — это то, что первые не имеют стека, а вторые — имеют.

Куда выходят? Когда? Куда возвращаются? Причем здесь стек? Причем здесь вообще потоки ("нити"), если async/await не завязан на них и будет спокойно работать в среде, где понятия потоков может вообще не быть?

Ваши async functions — частный случай coroutines, которые также известны как lightweight threads. Как вы думаете почему?

Нет. Coroutines — это кооперативная многозадачность с планировщиком, работающим в пользовательском коде. Нити имеют свой стек, и переключение между ними производится явно.


А асинхронные функции — просто функции, которые компилируются в state machine возвращают Task<>. Просто синтаксический сахар для событийно-ориентированного программирования, и ничего больше. Планировщик может задачи выполнять как кооперативно, так и параллельно, в зависимости от реализации. Если, например, нет ни IOCP, ни epoll/kqueue, то ему ничего не останется, как запускать задачи параллельно.

Асинхронные функции — не более чем stackless coroutines.


Что thread, что stackfull coroutine, что stackless coroutine имеют пачку кода, расположенную последовательно и указатель на текущий кусок из этой пачки. Логически это сущности одного порядка. stackfull имеет бонусом стек, stackless — вырожденный случай stackfull с фиксированным размером стека = 1. Вот и всё различие. Исполняться они все могут хоть на одном ядре, хоть на нескольких, хоть по очереди, хоть попеременно — это уже во власти планировщика и расставленных yield и await.

А какое отношение это имеет:


  1. к вопросу о том, как связаны понятия потоков (thread, нить) и async/await?
  2. к вашему изначальному утверждению, что от использования await исполнение внезапно становится синхронным? Я так и не увидел аргументов к этому утверждению.

Код синхронный, когда исполнение идёт последовательно с ожиданием завершения подзадач. Используете ли вы для синхронизации sync call, await или thread.join — совершенно не важно.


Смотрите, совершенно синхронный код, который при этом исполняется на всех ядрах процессора:


enum n = 1_000_000;
enum delta = 1.0 / n;

alias getTerm = (int i)
{
    immutable x = ( i - 0.5 ) * delta;
    return delta / ( 1.0 + x * x ) ;
};

immutable pi = 4.0 * taskPool.reduce!q{ a + b }( n.iota.map!getTerm );

assert(pi.approxEqual(3.1415926));
Код синхронный, когда исполнение идёт последовательно с ожиданием завершения подзадач.

Видимо, ваш "синхронный код" — это вообще не то, что синхронные/асинхронные операции в общепринятом понимании. Приведите пример "асинхронного кода" в вашем понимании.


И по-вашему, если я начал долгую асинхронную операцию IO, потом долго что-то считал, и потом "жду" результата начатой ранее операции через await — это типа все синхронно?

потом "жду" результата

Это и называется "синхронизация". Без неё ваш код становится асинхронным.

Неужели? То есть, по вашему, первый код синхронный, а второй нет?


// 1
async Task Example1()
{
    await DoSomethingAsync();
    DoSomethingElse();
}

// 2
Task Example2()
{
    var t1 = DoSomethingAsync();
    return t1.ContinueWith(t => DoSomethingElse());
}

И не уходите от вопроса. Что в вашем понимании асинхронный код?


Вообще асинхронность подразумевает, что что-то происходит независимо от основного потока выполнения. Использование await соответствует этому определению, потому что фактически обозначает:
То, что после await, выполнится тогда, когда задача, на которой сделан await, завершится (что является асинхронным событием). А сейчас возвращаем управление тому, кто нас вызвал.

Да, во втором случае, когда мы выходим из функции колбэк ещё не отработал.

Сюрприз, в первом тоже. При вызове await происходит выход с незавершенной задачей. Это один и тот же код, написанный по разному.

Классный сюрприз — забыл await и всё сломалось.

Классный сюрприз — забыл if и все сломалось.

Поэтому ниже вы предлагаете вообще отказаться от async/await. Тогда-то ошибки не будет, просто сломается и починить нормально нельзя будет.


Вы уходите от первоначального вопроса: о том, что это один и тот же код и что он может быть в обоих случаях синхронен или асинхронен (в вашем понимании), но точно не в одном случае так, а в другом иначе.


И "забыл await" — это совершенно другой вопрос. Для этого есть инструментарий, который показывает предупреждения, для этого есть суффикс Async, который намекает на необходимость await, и все такое. Никто не мешает во втором примере вернуть t1, что будет точно такой же ошибкой.

Ложная уверенность — хуже её отсутствия.


А предупреждения можно к любому варианту выдавать.

Классный сюрприз — забыл await и всё сломалось.

Умная среда разработки и компилятор напомнят программисту о том, что он дурак — вызвал функцию и не использует её результат.

Вообще асинхронность подразумевает, что что-то происходит независимо от основного потока выполнения. Использование await соответствует этому определению, потому что фактически обозначает:
определение не полное, именно поэтому вы путаете многопоточность и асинхронность.

Асинхронность (в случае локального, выполняющегося на одной машине кода) как правило говорит о том, что вы умеете реагировать на события/сообщения/уведомления от «того, что происходит независимо от вас» — когда что-то стороннее начинает вас уведомлять — и у вас как правило есть «обработчики»/«подписки»/«хуки»/«коллбеки»… — которые будут вызваны когда событие произойдет).

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

То, что после await, выполнится тогда, когда задача, на которой сделан await, завершится (что является асинхронным событием). А сейчас возвращаем управление тому, кто нас вызвал.

завершение процесса — не стоит называть асинхронным событием.
Вот если бы ваш таск присылал вам в случайные моменты времени сообщения, и вы на них реагировали — это было бы асинхронным событием.

А так — это поведение, сходное с обычным «семафором».
Сравните вашу фразу с фразой "то, что после «вызова mуSemaphore.release(), выполнится только тогда, когда сосдений поток освободит семафор».

Но семафоры — это обычная синхронизация потоков, а ни какая ни «асинхронность».

Хорошо, такой вопрос, майкрософт в своей документации говорят, что async-await механизм асинхронного программирования, вы же очевидно утверждаете обратное. Вопрос — как вы считаете, майкрософт нарочно вводит людей в заблуждение или они просто неграмотные?

документация майкрософт… это очень скользкая штука. двусмысленнее этого только рекламные объявления.

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

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

там есть некая «регистрация продолжения (»continuation") про которыую никто не знает что это именно аткое.

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

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

в части документации — я за ними лет 20 наблюдаю и поверьте мне — они легко пойдут на подмену понятий и назовут «теплое» «мягким», если это повысит продажи, или поможет сформировать в головах своих разрабов некую «узкокалейность майкрософтвей», наплевав на то, что во всем остальном мире это именно «теплое».

узкокалейность мышления — это примерно как с 1с — но последним хоть простительна узкокалейность, потому что у них свои уникальные механизмы (кстати, очень грамотно методически продуманные), и они не создают путаницу в терминах — «регистры учета» вот есть только в 1с, а общепринятые термины импользуют именно так, как это принято везде. вот1 или вот2. это понимание точно такое же как скажем в js.

на мисте, кстати, очень правильная фраза есть:
1с не реализовала неявное преобразование обычного кода в асинхронный, переложив тяжесть реализации асинхронности на программиста.
.

а вот майкрософт, похоже… реализовало. но вот что?)))

То, что вы называете "асинхронным программированием" с легкой' руки майкрософта, никакое не асинхронное програмирование, а скорее «неявное преобразование» обычного/синхронного кода во что то иное при компиляции.

теоретически, байткод может быть и асинронным, но на этот счет в указанной документации нет ни каких иных признаков, кроме «самопровозглашения что это мол асинхронное»: нет ни слова про обработчики событий, ни коллбеки…

есть, повторюсь, некая «регистрация продолжения»(continuatiin)" но такого технического термина нет, и что имеется в виду — никто не знает, и додумки ваших коллег про то что это выкидывается в коллбек-функцию — пока не подтверждены ничем.

сейчас, снова повторюсь, я бы классифицировал await/async как некий мехапизм «отложенных заданий».

Но это никак не асинхронный механизм.

Потому что там даже «асинхронного запуска» (типа запустили, оно начало работать, а мы отвалились) нету — потому что и параллельности то нет: согласно опять же этой документации, асинк / await не требуют многпоточностиок значит он не выполняется параллельно. последнее кстати, очень подозрительно и смахивает на ошибку.

я конечно не знаю деталей этой магии, но фраза
Асинхронные методы не требуют многопоточности, поскольку асинхронный метод не выполняется в своем собственном потоке. Он выполняется в текущем контексте синхронизации и использует время в потоке, только когда метод активен
… wtf?! вы серьезно?)))

так в каком потоке выполняются асинк-методы, из какого потока они выбирает время, если многопоточности нет, а в своем потоке он не выполняется. есть тайный тайный поток, про который забыли рассказать в документации и который не входит в понятие многопоточности? (это как история с пользователем Администратором, который на самом деле не совсем администратор, и может не всё, а есть еще более «администраторный администратор»)

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

в общем я не знаю кто кого обманывает, но, как минимум, называть использование отложенных заданий, с хитрым «магическим» процессом исполнения — асинхронным программированием — это подмена понятий.
документация майкрософт… это очень скользкая штука. двусмысленнее этого только рекламные объявления.

Значит все-таки врут. Как и мозилла, и куча других языков с аналогичным функционалом. Просто заговор какой-то


например там нет ни слова про коллбеки, или событийность, нет ни слова про асинхронный запуск (коим можно конечно классифицировать запучк процедуры в параллеьном потоке, но в асинк… типа же нет потоков(?)...)

То есть асинхронность это что-то с коллбеками? :) Никогда не встречал такого определения. Наверное, все же бывает и по-другому.


там есть некая «регистрация продолжения (»continuation") про которыую никто не знает что это именно аткое.

Ну если вы не знаете, не надо выдавать свою неграмотность за проблему языка.


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

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


Потому что там даже «асинхронного запуска» (типа запустили, оно начало работать, а мы отвалились) нету — потому что и параллельности то нет: согласно опять же этой документации, асинк / await не требуют многпоточностиок значит он не выполняется параллельно. последнее кстати, очень подозрительно и смахивает на ошибку.

Наверное, потому что многопоточность и асинхронность это разные вещи, м?


сейчас, снова повторюсь, я бы классифицировал await/async как некий мехапизм «отложенных заданий».
Но это никак не асинхронный механизм.

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


так в каком потоке выполняются асинк-методы, из какого потока они выбирает время, если многопоточности нет, а в своем потоке он не выполняется. есть тайный тайный поток, про который забыли рассказать в документации и который не входит в понятие многопоточности? (это как история с пользователем Администратором, который на самом деле не совсем администратор, и может не всё, а есть еще более «администраторный администратор»)

Там понятно написано для человека в теме, что такое контекст и какое отношение он имеет к потокам.


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

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


в общем я не знаю кто кого обманывает, но, как минимум, называть использование отложенных заданий, с хитрым «магическим» процессом исполнения — асинхронным программированием — это подмена понятий.

Никакой магии нет, просто компилятор сам переписывает код на коллбеки, чтобы программист не парил себе мозг этими простынями и что компилятор превращает такой код, который просто и понятно читается человеком
image
В такой
image
Где начинается чехарда с обработкой ошибок, непонятно что где происходит и непонятным дебаг экспириенсом. Вот презенташка целиком.


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

по порядку ))
Значит все-таки врут. Как и мозилла, и куча других языков с аналогичным функционалом. в документации мозилла и в документации майкрософт одинакового только слово async.
Просто заговор какой-то

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

То есть асинхронность это что-то с коллбеками? :) Никогда не встречал такого определения. Наверное, все же бывает и по-другому.
асинхронность, это event-driven стиль построения системы. коллбеки — это наиболее частый способ указания на функцию обработчик.

у вас нет в коде событий и функции обработчика событий? значит ваш код — ни разу ни асинхронный, потому что он построен не по асинхронной логике.

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

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

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

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

да, await волшебным образом не блокирует текущий поток, но это иные свойства, не связанные event-driven стилем разработки.

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

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

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

еще раз повторюсь: то, что await (вероятно) приводит к асинхронному байткоду — это результат компиляции. я могу вам постороить компилятор, который будет преобразовывать код в набор runnable (скажем до await и после) и выполнять эти раннаблы в пуле потоков, итоговое поведение будет точно точно таким же — это не будет блокировать ваш гуи поток, все что после await будет выполняться строго после завершния того раннабла, в который я засуну сам await, но это ни разу не будет асинхронный или синхронный байткод. это нечто иное.

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

И в чем же вы видите принципиальную разницу между методами then и ContinueWith?

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

Двойная буква ять. await в js — сахар над Promise#then, работающий поверх Promise (с автоматической конвертацией не-Promise в resolved promise. См. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await.


То, что в шарпе не отличается ничем, кроме имён классов и методов.

не верно, потому что await не регистриурет никаких действий, и его поведение, на уровне исполнния последовательности строчек кода, ничем не отличается от работы семафора.
да, await волшебным образом не блокирует текущий поток, но это иные свойства, не связанные event-driven стилем разработки.

Во-первых, принципиальное отличие await от семафора — отсутствие блокировки. Во-вторых, "волшебным образом" он это делает потому, что это по сути и есть callback, о чем вам сто раз сказали. Но вы, видимо, знаете, как работает await, лучше, чем официальная документация и целая куча специалистов и экспертов.


действия выполняющиеся вне какого-либо потока — нонсенс, противоречит основам работы операционной системы

Вам бы Таненбаума почитать или Руссиновича. Некоторые действия выполняются ОС на таком уровне, где понятия потоков и процессов нет вообще. Это к слову. И есть, например, такая штука, как DMA, которой программно ставится задание что-то где-то переместить в памяти, и она потом это делает сама независимо от процессора, который в это время может делать что угодно другое. Каноничный пример асинхронной операции, для выполнения которой не требуется никакого потока, о чем и была речь в статье.

И есть, например, такая штука, как DMA, которой программно ставится задание что-то где-то переместить в памяти, и она потом это делает сама независимо от процессора, который в это время может делать что угодно другое.

DMA — это не выполнение вычислений описанных у вас в async функции.
Когда у вас вычисления внутри вызываемой через await функции — они выполняется внутри какого-то потока.

и выполняемое операционкой действия глубоко внутри — тоже формально прикреплены к какому-либо потоку. то не имеет отнощения к мифу о внепоточности кода внутри await
Когда у вас вычисления внутри вызываемой через await функции — они выполняется внутри какого-то потока.

Вычисления — это что? Вся полезная нагрузка или именно обычные вычисления на CPU? Если вся полезная нагрузка, то вы ошибаетесь, потому что полезная нагрузка может выполняться в том числе на DMA и подобных механизмах. Если вы только про вычисления на CPU вроде решения всякого матана — ну да, это в конечном итоге делает какой-то поток, и что? Основной профит от асинхронности идет за счет асинхронного IO, который процессор как раз вообще не занимает и никаких потоков там нет.


и выполняемое операционкой действия глубоко внутри — тоже формально прикреплены к какому-либо потоку

Потоки в ОС появляются на определенномуровне абстракции. На уровнях ниже есть драйверы, прерывания, DPC, которые работают "просто на процессоре" и никаких потоков там нет.

Чукча не читатель, видимо, ведь в статье все подробно было разжевано.

Что такое "код внутри await"? Можете пояснить на примере?

Во-вторых, «волшебным образом» он это делает потому, что это по сути и есть callback, о чем вам сто раз сказали. Но вы, видимо, знаете, как работает await, лучше, чем официальная документация и целая куча специалистов и экспертов.

вот как раз в документации то, ничего про callback не сказано.
Более того — там несется некая ахинея про код, выполняющийся вне основного потока, но без потока. Я согласен что возможно, async таск выполняется вне потока вашей программы, а в неком «сервисном потоке», который предоставляется .net-средой -исполнения. Но если у вас есть вычисления внутри async функции, они будут выполняться внутри потока.
А в документации которую вы показали рассказывается о некой «магии», и ни слова о том, как это действительно проиходит. Потому вы или покажите офф. документацию где явно сказано, что там действительно последующая часть кода выносится в call-back-функцию, или признайте что вы «не совсем правы». Потому что повторюсь — в документации рассказывается о неких «contonuation» — а что это — нигде не написано.

Более — того. По большому счету не важно, что там внутри. Считаете что там создается асинхронный байткод — ок, я допускаю, я не против. Я против того, что бы на основании того, _во_что_ превращается await в машинном коде, классифиировать исходный код.

Await позволяет вам создавать код в синхронном стиле, который при компиляции создает асинхронный. Да, он добавляет в эту синхронность особые точки в которых поток может заняться другими задачами пока выполняется код вызванный через await. Да, такое особое свойство. Но код последователен, синхронен, событий и обработчиков в нем не объявлено. Значит это синхронный код.

А то что вы считаете что при синхронности должен «блокироваться поток» — это следствие того, что вы привязываете определение к конкретной реализации.

бритва оккама, говорите? если код построен по логике последовательного выполнения операций, синхронного, выглядит как синхронный, выполняется как синхронный. подчинятся логике синхронного — значит код синхронный.

Исходный код. но не байт код, который получаем в итоге компиляции.

не верно, потому что await не регистриурет никаких действий, и его поведение, на уровне исполнния последовательности строчек кода, ничем не отличается от работы семафора.
да, await волшебным образом не блокирует текущий поток, но это иные свойства, не связанные event-driven стилем разработки.


Во-первых, принципиальное отличие await от семафора — отсутствие блокировки.

давайте ещё раз)))
то что await, приводит к тому, что перебор операций исходного кода в данной точке приостанавливается (но поток не блокируется) — это хорошее свойство но с точки зрения поведения, эта логики кода, к понятию «асинхронность» он имеет отношение косвенное, связанное только с тем как оно реализовано.

Повторюсь — как вы будете классифицировать этот код, если он будет собран компилятором, который разнесет ваш код на 3 Runnable (до await, сам await, после await ), сложит их в очередь, и поставит на выполнение для некого планировщика? плаировщик будет брать на выполнение следующий раннабл только тогда, когда выполнится предыдущий.
первый и последний runnable он выполнит в gui потоке, сам await — в сервисном/фоновом.

Поведение будет точно таким-же.
«GUI-Поток» не блокируется, операции выполняются строго в нужном порядке, ждут друг друга без блокировки gui потока.

Классификация исходного кода изменится или нет? можете ответить на этот вопрос?
Поведение будет точно таким-же.
«GUI-Поток» не блокируется, операции выполняются строго в нужном порядке, ждут друг друга без блокировки gui потока.

Классификация исходного кода изменится или нет? можете ответить на этот вопрос?

при этом отмечу, что никаких асинхронных call-back в этом случае нет. нет ни обработчиков, ничего. Просто конвеер задач на входе планировщика.

от такой реализации — исходного ли «синхронность» исходного кода?
вот как раз в документации то, ничего про callback не сказано.
А в документации которую вы показали рассказывается о некой «магии», и ни слова о том, как это действительно проиходит. Потому вы или покажите офф. документацию где явно сказано, что там действительно последующая часть кода выносится в call-back-функцию, или признайте что вы «не совсем правы». Потому что повторюсь — в документации рассказывается о неких «contonuation» — а что это — нигде не написано.

Запросто: https://docs.microsoft.com/en-us/dotnet/standard/asynchronous-programming-patterns/consuming-the-task-based-asynchronous-pattern
Under the covers, the await functionality installs a callback on the task by using a continuation. This callback resumes the asynchronous method at the point of suspension.


И еще: https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/chaining-tasks-by-using-continuation-tasks


In asynchronous programming, it is very common for one asynchronous operation, on completion, to invoke a second operation and pass data to it. Traditionally, this has been done by using callback methods. In the Task Parallel Library, the same functionality is provided by continuation tasks. A continuation task (also known just as a continuation) is an asynchronous task that is invoked by another task, which is known as the antecedent, when the antecedent finishes.


Но код последователен, синхронен, событий и обработчиков в нем не объявлено. Значит это синхронный код.

Где вы вообще взяли термин "синхронный код" и что это значит? Какая разница, написано оно текстом через события и обработчики или последовательно, если результат одинаковый? Синхронность/асинхронность — свойство выполняемых операций, а не текста, их представляющего. Мне без разницы, как писать код: через await или на callback-ах, результат будет одинаковый. Разве что на await это читаемо, а на callbackах мозг можно сломать.


Классификация исходного кода изменится или нет? можете ответить на этот вопрос?

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

"Синхронизация" и "синхронность" — разные термины, вообще-то, а вы их путаете.


Уместнее использовать термины "блокирующий" и "неблокирующий", тогда путаницы не будет.


А синхронизация — это механизм обеспечения порядка выполнения инструкций, и ей все равно, какой это код.

Знаете, я тут ради интереса поискал по странице, и слово "синхронизация" потребляется исключительно вами, vintage и dplsoft. Сдается мне, что не я путаю эти термины потому что я все это время говорил про совершенно другой.

Вы сможете дать определение "синхронности" без использования слова "синхронизация"?

"Синхронно" — это исполняется прямо здесь и сейчас.


"Синхронизация" — не имеет отношение к асинхронности. Вроде как общепринято означает использование семафорв/мьютексов/спинлоков/watever чтобы потоки не покусали общие данные.

Мне, в принципе, нравится идея отсюда:
https://toster.ru/q/125725

Код называется «синхронным», если он использует только блокирующие операции операционной системы, такие как ввод-вывод, ожидание по таймеру, ожидание события, например, завершение потока.

Когда же код использует только неблокирующие операции, то такой код является «асинхронным».

Ну если же код использует одновременно и блокирующие, и неблокирующие операции, то такой код называется «говнокод».

И самое главное: языковая конструкция `await` не является вызовом к операционной системе, поэтому её появление в коде не позволяет судить о том, является ли код асинхронным или нет.
Я бы лучше поделил код на «последовательный» и «не последовательный».

Параллельно выполняемый код — это был бы частный случай «не последовательного» кода, другой пример не последовательного кода — это каллбэк. А синхронизация — это способ сделать не параллельный код последовательным.

К сожалению, у нас много книг написано про параллельное выполнение, но никакого внимания не уделяется «не последовательному». А между тем это гораздо более частый случай учложнения наших приложений.
Вернее, «синхронизация — это способ сделать параллельный код последовательным»

*в некоторых отдельных местах, но в целом код останется параллельным. (1000 потоков скачивают файлы и увеличивают счетчик на 1 — скачивают параллельно, счетчик увеличивают последовательно)

А если вы останавливаетесь на текущей инструкции, до тех пор пока не выполнится что то независимое от вас — это синхронный код.

Сколько еще раз надо написать, что при вызове await не происходит остановки, а просто все после await фактически становится callback-ом? Это ровно настолько же синхронизация, насколько синхронизацией является подписка на событие завершения асинхронной операции и выполнение какой-то операции в качестве реакци на это событие. Если это в вашем понимании синхронизация, то пусть так. Но наличие такой "синхронизации" не делает код синхронным. От того, что две асинхронные операции "синхронизированы" так, что одна начинает работать после завершения другой, их выполнение синхронным не станет. Или вы будете с этим спорить?


завершение процесса — не стоит называть асинхронным событием.
Вот если бы ваш таск присылал вам в случайные моменты времени сообщения, и вы на них реагировали — это было бы асинхронным событием.

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

И по-вашему, если я начал долгую асинхронную операцию IO, потом долго что-то считал, и потом «жду» результата начатой ранее операции через await — это типа все синхронно?
это запуск потока с последующей синхронизацией потоков.
И да, это, по логике/стилю работы работы — синхронный код.

Приведите пример «асинхронного кода» в вашем понимании.

а-синхронный код, в классическом, верном, понимании — это суть, событийно ориентированный код, который построен на колл-беках и/или обработчиках событий

примеры асинхронного апи: WinApi функции работы с последовательными портами, в которые вы передаете ссылку на функцию, которую надо вызывать, когда на вход поступят байтики. Qt-шные сигнал-слоты. Промисес в js (потому что вы передаете им функцию/ссылку на функцию)

int main()
{   ...
    serial->setOnByteIncomeCallBack(&onByteInome);
    serial->writeBytes(....);
}

int onByteInome(byte bytes[])
{     ...
}
а-синхронный код, в классическом, верном, понимании — это суть, событийно ориентированный код, который построен на колл-беках и/или обработчиках событий

Ну и чем await не угодил? Это ровно то же самое. Визуально (то есть в исходнике) он выглядит как синхронный код с ожиданимями и всем таким, потому что так и задумано. Фактически же await обозначает, что все ниже него будет callback-ом. Довольно грубое описание, но концептуально это ровно так.

Кто выглядит как утка и крякает как утра — того и называют уткой. Даже если внутри у неё конечный автомат и колбэки.

Такой вопрос задам просто, зачем нужна асинхронность? Как вы понимаете её задачу?

Да не нужна она. Даже в JS, на хайпе которого она выехала, её уже закапывают.

Думаю, на этом можно спор и закончить.

Ничего себе. То есть, например, если надо опросить 1000 сервисов на предмет курса валют или прогноза погоды, то надо делать это строго синхронно, по-очереди, дожидаясь ответа от каждого, а не поставить 1000 асинхронных задач одновременно? Сильное утверждение, поржал.

Я боюсь, в этом случае кстати надо использовать не асинхронность, а многопоточность, ибо как имеет смысл в одном потоке опрашивать по 10-20 серверов + барьерная синхронизация, а не делать 1000 тасок. Ну чисто так… Я бы так сделал по крайней мере.

Это же частности. Я бы поставил 1000 тасок, а там планировщик сам разберется, как их раскидать по потокам, создать ли 10, 20, 50 потоков или вообще все дать в одном. Это зависит в том числе от железа. На 1-ядерном ПК нет смысла заводить 20 потоков.
И в конце-концов неважно, один поток будет делать 1000 запросов к сервисам или несколько, если время на инициализацию асинхронного запроса заведомо меньше, чем время ответа сервиса.

> Я боюсь, в этом случае кстати надо использовать не асинхронность, а многопоточность, ибо как имеет смысл в одном потоке опрашивать по 10-20 серверов + барьерная синхронизация,

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

Ну ладно ладно, убедили, молчу :(

> Да не нужна она. Даже в JS, на хайпе которого она выехала, её уже закапывают.

Закапывают её только люди, которые эту асинхронность не понимают по причине недостатка образовательной базы. Например, не наигрались с потоками в C++, не пользовали функции-генераторы в C# и т.д.

Ни фига себе — выглядит как утка. Синхронный метод вернёт byte[], а асинхронный — Task<byte[]>.

getDataSync() и await getData вернут одно и то же.

Погодите. То есть вы считаете, что если вы пишете await Task<int> и результат выражения можно присвоить в переменную типа int, то вызов блокирующий?

Синхронный.

> getDataSync() и await getData вернут одно и то же.

За одной лишь разнице, что во втором случае сама функция, в которой вызывается getData, будет возвращать Task, тогда как в первом случае это недопустимо. И выход из функции во втором случае произойдёт на await, а не по завершении выполнения всей функции.

Внезапно


async Task<int> ReadBytesAndGetWrittenByteCountAsync()
{
    byte[] bytesRead = await serial.ReadBytes(); // читаем с устройства долго и асихронно
    return bytesRead.Length; // возвращаем количество записанных байт
}

Эквивалентен:


async Task<int> ReadBytesAndGetWrittenByteCountAsync()
{
    Task<byte[]> bytesReadTask = serial.ReadBytes(); // читаем с устройства долго и асихронно
        var bytesCountTask = bytesReadTask.ContinueWith(x => x.Result.Length);; // когда таска выполнится, будет вызван коллбек и мы посчитаем количество байт
    return bytesCountTask // возвращаем задачу, на которой пользователь будет ждать
}

Что весьма похоже на ваш пример с onByteInome

О, Qt, хорошо, что вы вспомнили.

У меня поверх кутешного QFutureWatcher с сигналами и по-вашему асинхронным кодом написана монадическая обёртка, которая позволяет писать что-то вроде
Sequence(QtConcurrent::run(func)) >>
    [] (auto result1) { return anotherFuture(result1); } >>
    [] (auto result2) { return smthElse(result2); };

и так далее.

Это асинхронный код?

Если я теперь возьму предложение Саттера по метаклассам, какое-нибудь ещё предложение по трансформации AST и наваяю что-то такое, что позволит переписать мне этот код как
await auto result1 = QtConcurrent::run(func);
await auto result2 = anotherFuture(result1);
return smthElse(result2);

будет ли этот код синхронным или асинхронным?

Для await есть отдельное предложение, C++ Extensions for Coroutines (ранее называлось Resumable Functions)

Да, тоже хороший пример.

Просто когда это сделано в языке, а не либой, это не так интересно.
Я бы хотел разобраться в вашей терминологии:
1. Вот это асинхронный код? Если нет, то что здесь блокируется?
def getBonusesByPersonEmail(email: Email): Future[List[Bonus]] = {
  val person: Future[Person] = db.findPerson(email)

  val purchases: Future[List[Purchas]] =
    person.flatMap(p => storeRestServce.getPurchases(p.id))

  val bonuses: Future[Seq[Bonus]] =
    purchases.flatMap(ps => Future.sequence(
      ps.map(p => bonusesSoapService.getBonus(p.promoCode))
    ))

  val validBonuses: Future[Seq[Bonus]] = bonuses.map(_.filterNot(_.expired))
  return validBonuses
}


2. А этот? Если нет, то где здесь «блокировки»?
def getBonusesByPersonEmail(email: Email): Future[List[Bonus]] = for {
  person <- db.findPerson(email)
  purchases <- storeRestServce.getPurchases(person.id)
  bonuses <- Future.traverse(purchases)(p => bonusesSoapService.getBonus(p.promoCode))
} yield bonuses.filterNot(_.expired)


3. А вот этот? (Тот же вопрос про «блокировки»)
def getBonusesByPersonEmail(email: Email): Future[List[Bonus]] = async {
  val person = await{ db.findPerson(email) }
  val purchases = await{ storeRestServce.getPurchases(person.id) }
  val bonuses = await{
    Future.traverse(purchases)(p => bonusesSoapService.getBonus(p.promoCode))
  }
  bonuses.filterNot(_.expired)
}

К сожалению я не знаком с этим языком, так что ничего определённого сказать не могу.


Тут я подробней всё это расписывал: https://habrahabr.ru/post/307288/

С чем знакомы? Java? Haskell? Знаете что такое монада?
Просто первый и последний примеры должны читаться и без знания scala. Достаточно понимать, что — Future — аналог CompletableFuture из Java или Task из C#. Методы map и flatMap работают так, как предполагается у монады.
Второй можно пропустить (аналог do-notation из haskell).

Первый асинхронный, ибо колбэки вызываются чёрт знает когда.


Последний синхронный с явно обозначенными точками синхронизации.

Вы удивитесь, но написано абсолютно одно и то же.


Если на пальцах, то:


async Task<...> A()
{
   ...
   var x = await B();
   ...;
}

эквивалентно


Task<...> A()
{
   ...
   return B().ContinueWith(() =>
   {
   });
}

Значит я не правильно понял последний код и он тоже асинхронный. В принципе весь FP — об асинхронном исполнении.

Но именно так работает async/await в Scala/C# и JS — вешает весь код дальше в колбек. На Future, Task и Promise соответственно.
Не находите, что все ваши рассуждения в этой ветке теряют смысл?