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

Начнем с того, что я не пишу на JavaScript профессионально. Я много пишу на TypeScript, считаю что разбираюсь во frontend-разработке (например сейчас я занимаюсь интеграцией собственной реализации VDOM в свой датагрид-движок Lattice, о котором скоро будет отдельная статья. Ну как скоро… будет, в общем :), однако мой основной профиль — C# и делать всё, чтобы защитить бедных C# разработчиков от излишнего ныряния в JavaScript.

Так вот. Прошу обратить внимание, что я решительно не имею ничего против JavaScript как такового и его технической экосистемы. Это нормальный язык, с приемлемым тулсетом. Во всяком случае мне в нем комфортнее чем в каком-нибудь… LabView. Я не считаю его уж шибко плохо сделанным — в конце концов в нем нет несовместимых с жизнью недостатков, но однако и ничего революционного или магического я в нем так же не нахожу. Мой основной посыл — к сообществу. К людям, а не к технологии, ибо как с моей точки зрения, большинство проблем и холиваров вокруг JavaScript происходит не от самого языка, а от его, как мне иногда кажется, обезумевших последователей. Имея за плечами продолжительный опыт общения с разработчиками на JavaScript, я предположил что этот язык работает как мыслевирус, который со временем превращает пишущего на нем человека в агрессивного фанатика, чего мне не удалось отметить с другими технологиями. Собственно, эта статья является сатирой на штампы JavaScript-разработчиков, которых JavaScript поразил как мыслевирус. Отсюда и название.

И да. Я не считаю что все поголовно JavaScript-разработчики такие. Среди них много нормальных и адекватных людей, которым удалось (или не удалось :) остановиться на каком-либо квалификационном уровне и сохранить трезвость ума и здравость памяти. Честь им и хвала. Однако же, фанатики от JavaScript-а по своей природе — гораздо более «громкие» (так всегда: кто-то кричит, а кто-то работой занимается) и настолько фееричны, что заслуживают отдельного рассмотрения. Так же в статье будет упоминаться jQuery. Так вот — я ни в коем случае не агитирую писать всё на нём и не использовать более продвинутые решения. Используйте то, что нравится, но пожалуйста, оставайтесь при этом людьми. Итак…

Дорогой JavaScript-разработчик!


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

0 уровень: «jQuery — хороший»


Симптомы:

— JavaScript тебя интересует только по работе;
— Ты можешь написать кусок кода в теге <script>;
— У тебя нет особого желания говорить о JavaScript, кроме как иногда жаловаться на него коллегам в курилке. Ты вообще его и за язык-то не считаешь;
— jQuery — удобный инструмент и искренне тебе помогает в делах;
— JavaScript написан в резюме одной строчкой наряду с Java, C/C++ и SQL.

Диагноз:

Здоров. У тебя все хорошо, мозг не задет, проблем не наблюдается. Скачай сериал и открой пиво — ты хорошо поработал на этой неделе.

1 уровень: «Я просто сижу там, где JavaScript-ят»


Симптомы:

— Тебя забавят замыкания в JavaScript, ты испытываешь легкую гордость от того, что понимаешь как они работают;
— Ты раскладываешь JavaScript-код по файлам, поскачивал кучу плагинов к jQuery, думаешь об изучении AngularJS и чувствуешь успокоение и расслабление;
— Читаешь мануалы по плагинам, в соседней вкладке браузера у тебя обычно порнушка;
— jQuery — рулит и педалит, AngularJS отдается в голове звуками таинственного Туманного Альбиона;
— Ты знаешь о том, что '3' — 2 == 1, но '3' + 2 == '32' и это вызывает у тебя негодование.

Диагноз:

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

2 уровень: «Я могу бросить когда захочу»


Симптомы:

— Ты разбираешься в прототипном наследовании. Тебе немного мерзко, но в целом терпимо;
— AngularJS внедрен в домашний проект. Ты говришь начальнику что неплохо бы и в рабочем использовать;
— Читаешь мануалы по AngularJS, попутно изучаешь TypeScript/CoffeeScript. Изредка с интересом почитываешь рекламные статьи, рассказывающие о магии JavaScript и о том, как некая компания X с ним счастливо живет;
— jQuery тебя больше не заводит. Ты начинаешь использовать термины «MVW», «DSL», «DOM-дерево». JavaScript-объект называешь только «хэш»;
— Видео навроде JavaScript WAT больше не вызывают бурных эмоций. Ты понимаешь как это работает и почему.

Диагноз:

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

Здесь ты начал использовать npm



3 уровень: «Не вижу в этом ничего плохого»


Симптомы:

— AngularJS в продакшене твоего рабочего проекта. Не на всех страницах, но хоть так;
— Ты осознаешь что фронтенд — целая отдельная часть системы и невозможно же её поддерживать силами backend-разработчиков. Говоришь об этом с начальником, получаешь недоумевающий взгляд;
— Пишешь на TypeScript и нервно думаешь как бы автоматизировать сборку фронтеда. Читаешь статьи про grunt. Плотно знакомишься с опытом использования JavaScript в модных стартапах;
— Коллеги, в основном совмещающие backend с допиливанием скриптов на страницы проекта, перестают понимать что ты говоришь и назначают тебя ответственным за все, что касается клиентской части. Из-за этого у тебя появляется легкое негодование и небольшая обида на них;
— В резюме ты стираешь пункт JavaScript, и добавляешь новую секцию: Frontend.

Диагноз:

Твоя болезнь прогрессирует. Ты этого, скорее всего, не замечаешь, но просто поверь мне — вирус уже обосновался в твоей голове. Отложи JavaScript, попиши на C, освой какой-нибудь игровой движок. Тебе срочно необходимо отогнать от себя мысли о JavaScript, чтобы сдержать развитие болезни. На этой стадии еще можно взять себя в руки и быть просто хорошим fullstack-инженером.

4 уровень: «Вы просто не понимаете сути»


Симптомы:

— Сильное желание говорить о JavaScript. В курилке ты с горящими глазами рассказываешь недоумевающим коллегам о том, что JavaScript можно запускать вне браузера. Обижаешься, когда они не считают это серьезным достижением;
— Фронтенд рабочего проекта собирается отдельно, с использованием grunt и require.js. От этого тебе делается хорошо и приятно, появляется ощущение тепла и домашнего уюта. Коллеги не понимают что там происходит и почему именно так. На все вопросы отмахиваешься и отвечаешь нечто вроде «да там долго объяснять». Очень злишься, когда коллеги пишут что-либо в теге script;
— TypeScript становится тебе тесноват;
— Читаешь много концептуальных статей про будущее JavaScript. Узнаешь о существовании React, пробуешь его на домашнем проекте. Получаешь первый писательский опыт в комментариях к статье о React, находишь там поддержку и понимание;
— Подумываешь съездить на конференцию по JavaScript — встретиться с единомышленниками.

Диагноз:

Разжижение мозга в самом разгаре. Вирус самодовольно откусывает кусочки серого вещества и со смаком и хрустом пережевывает. Просто остановись, съезди в отпуск — желательно на Гоа, года на 2, научись медитации. Если ты не научишься контролировать свой JavaScript, то он убьет тебя и разрушит твою жизнь.

5 уровень: «JavaScript — няшка. А вы все — быдло»


Симптомы:

— Пробуешь React на домашнем проекте. Полоскаешь мозги начальнику, что весь ваш сайт надо срочно переделать на SPA и изоморфный рендеринг. Закономерно получаешь предупреждение о скором увольнении, но гордо хмыкнув увольняешься сам и уходишь в хипстерский стартап;
— TypeScript тебя сильно ограничивает в возможностях. Отказываешься от него не новом месте работы в пользу pure js;
— Любого, кто напишет тебе про '3' + 2 незамедлительно называешь безграмотным быдлом, которое просто не может осилить язык. Для тебя это уже очевидно. Начинаешь считать, что любой человек, у которого JavaScript не является основным языком работы — просто органически не способен понять прототипное наследование. Это — для избранных;
— Едешь на конференцию по JavaScript. Там тебе впервые показывают как за 20 секунд сделать сервер на Node.JS. Восторгаешься. Вирус, подпитавшись материалами с конференции откусывает ту часть мозга, которая еще помнила про многопоточность. Она помирает и не успевает задать свои вопросы;
— Считаешь что VDOM — неочевидное и гениальное решение, которое может прийти только в самые светлые головы этого мира.

Диагноз:

Болезнь подходит к необратимой стадии. Посмотри — вирус уже рушит твою жизнь. Немедленно достань учебник по C++ и выполни десяток-другой задач. С тобой становится невозможно общаться! Ты агрессивен и съехал с катушек. Я знаю, что ты в это не веришь, но окружающие всё видят.

Ты начал использовать Node.JS



6 уровень: «Это полноценный технический стек!»


Симптомы:

— Считаешь что весь web скоро перейдет на изоморфный рендеринг. Классические подходы устарели, а лично ты — стоишь на пороге технологической революции. Меняешь в резюме заголовок на «JavaScript-профессионал»;
— Раз за разом запускаешь сервер на Node.JS. Проверяешь его на самых разных задачах — блог, аналог твиттера, todo-лист, хостинг картинок. Поражаешься быстродействию. Считаешь что это и есть The Web Development;
— Любой эксперимент начинаешь с установки webpack-а, react-а и еще десятка пакетов. В процессе скачивания зачарованно смотришь на монитор;
— Забываешь зачем нужны SQL-базы. Искренне не понимаешь почему на mongodb нельзя делать вообще все. Авторов видео mongodb is web scale считаешь неграмотными толстыми троллями;
— Асинхронные операции в Node.JS для тебя — cutting edge. Ты полностью уверен, что ни в одном другом языке кроме как JavaScript это невозможно и никаким другим фреймворком кроме Node.JS это не поддерживается.

Диагноз:

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

7 уровень: «Мир должен знать!»


Симптомы:

— Пишешь свою первую статью об успешном внедрении Node.JS для решения, как тебе кажется, очень важных задач в вашей компании. В комментариях грубишь всем, кто посмеет усомниться в твоем успехе;
— Наотрез отказываешься слушать про другие языки и фреймворки. Предпочитаешь узнавать о них из разговоров с друзьями. Уверен, что C++ устарел и скоро его перестанут использовать. Java/C#/VB — это enterprise-языки и в них нет ничего кроме бессмысленного нагромождения паттернов, а работают они медленно потому что в них нет асинхронных операций. Вместо любых аргументов, что это не так — слышишь белый шум;
— JavaScript уже давно для тебя идеален. В нем нет недостатков, а если кто-то считает, что они есть — то только потому что этот кто-то не может открыть документацию и почитать, о чем ты ему незамедлительно сообщаешь;
— Социальная активность возвращается. Правда, ты не общаешься ни с кем, кто не входит в JavaScript-тусовку. Любого, кто задает тебе вопрос по JavaScript снисходительным тоном отправляешь «читать книжки» и упрекаешь в непонимании очевидного;
— Ты стал писать меньше кода. В основном ты проводишь время в чтении статей, расхваливающих JavaScript и пишешь одобрительные комментарии под ними. Называешь это «обмен экспертными мнениями» и «узкоспециальной профессиональной дискуссией».

Диагноз:

Критическое поражение мозга вирусом. Задеты важные мыслительные центры. Собери жесткие диски со всех компьютеров и выброси. Устройся работать грузчиком. Просто таскай коробки, пей, и ни с кем не разговаривай. Выброси свой Google Nexus и купи старенькую черно-белую нокию. Через несколько лет сможешь вернуться в программирование, начав изучение с С++. Не задавай вопросов — так надо.

8 уровень: «Вся индустрия только и была создана для JavaScript»


Симптомы:

— Для тебя в JavaScript-е лучшие пакетные менеджеры. Ты абсолютно уверен, что системы должны собираться из как можно более мелких пакетов по 10 строчек, а любая модульная декомпозиция строится по принципу «меньше модуль — значит лучше»;
— Считаешь что статическая сильная типизация — это тупиковая ветвь развития. Все более-менее типизированные языки скоро вымрут из-за своей негибкости и перегруженности. Ничего не должно ограничивать настоящего программиста — в том числе и система типов. Наследование, интерфейсы, абстрактные классы — пережиток прошлого, которые только все усложняют;
— … а JavaScript — вообще единственный язык, который избавляет человека от таких условностей. Он стоит на первых местах во всех рейтингах индустрии, развивается семимильными шагами и ничего кроме него не жизнеспособно. Ну может разве что TypeScript. И тот нужен только тем болезным, которые не осилили pure js;
— Ты забыл слово «многопоточность». Считаешь что это что-то уровня ассемблера и тебе это знать не нужно, ведь Node.JS полностью тебя от этого абстрагирует. Ты уверен что так и должно быть, а люди, которые в этом разбираются — или носители ненужной и бесполезной информации, или разрабатывают операционные системы;
— Ты чувствуешь, что вся IT-индустрия развивалась как-то неправильно. В ней много неуместных усложнений, которые никому не нужны. Ты искренне уверен, что все должно быть гораздо проще и что все люди, которые разрабатывали теоретическую базу для программирования — очень глупые, раз этого не понимали. И за что им только докторских степеней пораздавали? Ты бы справился гораздо лучше.

Диагноз:

Вирус целиком съел твой мозг и заменил его. Сожалеем, но тебя уже не спасти.

Комментарии (626)


  1. Aries_ua
    05.08.2017 22:58
    +20

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

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

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

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

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

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


    1. pnovikov Автор
      05.08.2017 23:05
      +5

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


      1. Aries_ua
        05.08.2017 23:10
        +5

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

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


        1. pnovikov Автор
          05.08.2017 23:13
          +6

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


          1. Aries_ua
            05.08.2017 23:19
            +3

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

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


            1. pnovikov Автор
              05.08.2017 23:20
              +4

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


              1. Aries_ua
                05.08.2017 23:24
                +3

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


                1. pnovikov Автор
                  05.08.2017 23:28
                  +2

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


          1. 0xd34df00d
            06.08.2017 05:05
            +4

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


            1. pnovikov Автор
              06.08.2017 06:52
              +9

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


              1. 0xd34df00d
                06.08.2017 09:38
                +8

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


                1. pnovikov Автор
                  06.08.2017 09:46
                  +2

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


                  1. 0xd34df00d
                    06.08.2017 09:49
                    +6

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

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


                  1. oldbie
                    06.08.2017 10:17
                    +3

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


                    1. 0xd34df00d
                      06.08.2017 10:20
                      +3

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


                      1. oldbie
                        06.08.2017 13:18
                        +2

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


          1. slonopotamus
            06.08.2017 12:52
            +15

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


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


            1. franzose
              07.08.2017 01:46

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


          1. Checkmatez
            06.08.2017 23:07

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


            1. APaMazur
              08.08.2017 16:11

              В случае 1С — это уже 8 уровень )))


        1. franzose
          07.08.2017 01:44

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


        1. AxeLWeaver
          07.08.2017 08:34
          +1

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


      1. TheShock
        06.08.2017 01:00
        +2

        Вот этот самый постфикс про идиотов и отличает фанатика от обычного разработчика

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


      1. Keyten
        06.08.2017 11:50
        +9

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


      1. NoMad42
        07.08.2017 11:05

        Тоесть это адаптация этой статьи на тему JS?)


    1. lpwaterhouse
      06.08.2017 01:22
      -12

      Пишу на JS уже лет 6-7

      Бедненький, такое не лечится.


  1. Iqorek
    05.08.2017 23:03
    -14

    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, очень легко, что то сломать и даже об этом не знать, потому что ошибок в консоли скорей всего не будет.


    1. pnovikov Автор
      05.08.2017 23:06
      +29

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


      1. Zoolander
        06.08.2017 04:18
        -4

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

        так что пускай люди пишут свои мнение
        и ради бога, не думайте за всех
        не пишите «никого не интересует»


        1. Stalker_RED
          06.08.2017 09:55
          +4

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

          Это в общем-то можно всё разрулить, но будет не она строчка, а десятка полтора. И по смыслу не будет отличаться от того, что внутри у jQuery.


          1. pnovikov Автор
            06.08.2017 09:57
            +6

            Знаком с проблемой не понаслышке, но никому об этом не говорю :)


            1. pnovikov Автор
              06.08.2017 14:08
              +8

              Заминусовали :( Вот именно поэтому и не говорю :(


          1. patricksafarov
            06.08.2017 11:38
            +8

            А мы вот возьмем и вынесем эту функцию в новую библиотеку — шах и мат.


            1. dom1n1k
              06.08.2017 13:32
              +6

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


              1. RifleR
                06.08.2017 17:24
                -1

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


                1. DistortNeo
                  06.08.2017 18:55
                  +1

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


                  1. staticlab
                    06.08.2017 19:06
                    +5

                    Справедливости ради, в webpack 2 поддерживается dead code elimination на уровне модулей, то есть import sortBy from 'lodash/sortBy' лишнюю часть лодэша при оптимизации отсечёт, если его правильно использовать.


                1. dom1n1k
                  06.08.2017 19:49
                  +1

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


        1. Hardcoin
          06.08.2017 13:49
          +2

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


      1. funca
        06.08.2017 15:31
        +5

        А еще «use jQuery» это известный мем на stackoverflow. По-моему в контексте статьи смотрится весьма органично. :)


    1. AnneSmith
      06.08.2017 02:47
      +2

      нет никаких трудностей, если для обращений к элементам вы используете только id вместо html разметки и css классов

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

      проблемы большого проекта решаются разработкой грамотной модели приложения, а не отказом от jquery


  1. Iqorek
    05.08.2017 23:13
    -5

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

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


    1. pnovikov Автор
      05.08.2017 23:15
      +20

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


      1. Iqorek
        05.08.2017 23:25
        -1

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


        1. pnovikov Автор
          05.08.2017 23:27
          +3

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


        1. TheShock
          06.08.2017 01:02
          -1

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

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


    1. mayorovp
      05.08.2017 23:57
      +9

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


      1. Tantrido
        06.08.2017 04:08

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


        1. NeonXP
          06.08.2017 05:16
          +30

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


        1. mayorovp
          06.08.2017 09:09

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


        1. Newbilius
          06.08.2017 15:19
          -6

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

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

          Хммм… действительно… в чём же разница… ;-)


        1. DistortNeo
          06.08.2017 16:16
          +2

          разве что дедлоков нет по Wait() или Result() — если синхронные операции вызываешь в асинхронном методе.

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


          1. PsyHaSTe
            07.08.2017 18:36
            -2

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


            1. mayorovp
              07.08.2017 20:32
              +1

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


  1. Graff46
    05.08.2017 23:26
    +3

    Я по причине обучения в ВУЗе столнулся с JS, до этого долго и упорно сидя на Lua. Ну первое время у меня матов не хватало, что бы описать JS, и если бы не jQuery, то возможно и сейчас бы не хватало.


    1. Zenitchik
      05.08.2017 23:58
      +6

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


      1. ivan386
        06.08.2017 10:27

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


        1. ZyXI
          06.08.2017 14:09

          «Всё как ключ» в общем удобно при работе с рекурсивными структурами. Не только сериализация, мне для конвертации в другие рекурсивные типы данных пригождалось (вроде из типов 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 в качестве основной версии просто нельзя.


        1. Zenitchik
          06.08.2017 21:33
          +1

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


          1. ivan386
            06.08.2017 23:24
            +1

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


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

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


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


            1. ZyXI
              07.08.2017 00:04
              +2

              В 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 — не знаю.


      1. egorcod
        06.08.2017 19:37
        +1

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


        1. ZyXI
          06.08.2017 21:25

          Как?то пытались использовать в 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. Другой пример.


  1. rail-ka
    06.08.2017 00:33
    +7

    У вас ссылка на «JavaScript как праздник» введет туда же, куда и «JavaScript как явление».

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


    1. pnovikov Автор
      06.08.2017 00:37
      +9

      У вас ссылка на «JavaScript как праздник» введет туда же, куда и «JavaScript как явление».

      Fixed


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

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


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

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


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

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


      1. maxpsyhos
        06.08.2017 05:56
        +6

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


        1. pnovikov Автор
          06.08.2017 06:50
          +1

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


          1. maxpsyhos
            06.08.2017 07:03
            -3

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


            1. pnovikov Автор
              06.08.2017 07:05
              +5

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


              1. maxpsyhos
                06.08.2017 07:08
                -5

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


                1. pnovikov Автор
                  06.08.2017 07:11
                  +6

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


            1. Hardcoin
              06.08.2017 17:11
              +6

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


      1. Keyten
        06.08.2017 12:00
        -3

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


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


        1. pnovikov Автор
          06.08.2017 12:09
          +12

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


          1. Antelle
            06.08.2017 12:22
            +5

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


            1. pnovikov Автор
              06.08.2017 12:30
              +3

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


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


              1. funca
                06.08.2017 15:48
                -9

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


                1. pnovikov Автор
                  06.08.2017 15:49
                  +4

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


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


                  1. funca
                    06.08.2017 16:57
                    -4

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

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

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


                    1. pnovikov Автор
                      06.08.2017 17:04
                      +1

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


                      1. funca
                        06.08.2017 17:18

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

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


                        1. pnovikov Автор
                          06.08.2017 17:23
                          +1

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


                          1. 0xd34df00d
                            06.08.2017 19:36
                            +2

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


                            1. pnovikov Автор
                              06.08.2017 19:59
                              +2

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


                            1. wheercool
                              08.08.2017 11:31
                              +2

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

                              Да, жизнь боль :(


                              1. 0xd34df00d
                                08.08.2017 21:05

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

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


                          1. funca
                            06.08.2017 21:50
                            +1

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

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


                1. Hardcoin
                  06.08.2017 17:16

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


                  1. funca
                    06.08.2017 17:33
                    +1

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


                    1. 0xd34df00d
                      06.08.2017 19:39
                      +1

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

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

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

                      Забавно, кстати, как раз сейчас нашёл баг в ghc, который выкидывает некоторые аннотации параллелизма.


                      1. funca
                        06.08.2017 21:25

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


                        1. 0xd34df00d
                          06.08.2017 22:27
                          +3

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


                          1. funca
                            06.08.2017 23:26

                            Сожалею, что пример оказался путанным. Я имел ввиду задачи, где глядя на код, результат не совсем всегда очевиден. В случае map fibonacci и задачи нахождения оптимального пути в графе в хаскеле получается неожиданно плохая асимптотика. По-моему это ни чем не лучше, чем приколы с приведением типов в JS.


                            1. 0xd34df00d
                              06.08.2017 23:45

                              Я имел ввиду задачи, где глядя на код, результат не совсем всегда очевиден.

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

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

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


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


                              1. mayorovp
                                07.08.2017 07:35
                                -2

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


                                1. 0xd34df00d
                                  07.08.2017 08:03
                                  -1

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

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


                1. franzose
                  07.08.2017 02:01

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


                1. potan
                  07.08.2017 13:02
                  +2

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


                1. SirEdvin
                  07.08.2017 18:31
                  +2

                  Многопоточность это сложно, а сложно это плохо

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


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


              1. S_A
                06.08.2017 16:12
                +1

                Многопоточность — это не плохо. И не хорошо. Просто в рассуждениях о 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 в линухах должен будет скопировать окружение процесса, проинициализировать процесс и прочие приседания на синхронизацию (где-то родительский поток ждет или поллит семафор заверешения чайлда).

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


                1. TimTowdy
                  06.08.2017 17:03
                  +3

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

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

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


                  1. pnovikov Автор
                    06.08.2017 17:06

                    чего-то более серьезного

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


                    1. TimTowdy
                      06.08.2017 17:32
                      +3

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


                      1. pnovikov Автор
                        06.08.2017 17:44
                        -2

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


                    1. mayorovp
                      06.08.2017 19:11
                      +2

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


                      1. grossws
                        07.08.2017 00:35
                        +1

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


                        1. mayorovp
                          07.08.2017 07:37
                          -1

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


                  1. S_A
                    06.08.2017 17:35

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

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


                    1. grossws
                      07.08.2017 00:36
                      +1

                      что для большинства современных задач (работаю в немаленькой веб-студии)

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


                  1. wert_lex
                    07.08.2017 16:06

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


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


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


                    1. pnovikov Автор
                      07.08.2017 16:48
                      +2

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


                      1. mayorovp
                        07.08.2017 17:01
                        +1

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


                        1. pnovikov Автор
                          07.08.2017 17:05
                          +1

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


                          1. mayorovp
                            07.08.2017 17:09
                            +1

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


                            1. pnovikov Автор
                              07.08.2017 17:14
                              +1

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


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


                              1. mayorovp
                                07.08.2017 17:22

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


                                1. pnovikov Автор
                                  07.08.2017 17:32

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


                                  1. justboris
                                    07.08.2017 18:47
                                    +1

                                    А зачем нужен сложный обмен сообщениями, чтобы построить 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);
                                    });


                                    1. mayorovp
                                      07.08.2017 20:50

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


                                      1. justboris
                                        07.08.2017 22:13

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


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


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


                                        1. wert_lex
                                          07.08.2017 22:29

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


                                          1. justboris
                                            07.08.2017 22:34
                                            -1

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


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


                                            1. pnovikov Автор
                                              07.08.2017 22:45

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


                                              1. mayorovp
                                                07.08.2017 23:00
                                                +1

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


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


                                        1. ZyXI
                                          07.08.2017 22:41

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


                                          1. justboris
                                            07.08.2017 22:43

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


                                  1. wert_lex
                                    08.08.2017 20:10
                                    +1

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


                                    1. pnovikov Автор
                                      08.08.2017 21:23
                                      +1

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


                              1. kekekeks
                                08.08.2017 16:39
                                +1

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

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


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


                      1. DistortNeo
                        07.08.2017 17:09
                        -2

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


                      1. wert_lex
                        07.08.2017 22:27
                        +3

                        Постойте, я и не говорю, что нода хороша для 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- файлики, то я бы предпочёл это действо передавать системе и вызывать отдельным процессом. Потому что наверняка есть хорошая сторонняя/системная тулза, которую не стоит пытаться запихнуть ни в ноду, ни в похапэ, ни в питон.


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


                        1. S_A
                          08.08.2017 04:38
                          -2

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


                          1. jetexe
                            08.08.2017 14:07
                            +3

                            Я бы взял какой-нибудь js-xslx и генерил бы файлики на клиенте

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


                            1. wert_lex
                              08.08.2017 15:48
                              +1

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


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


                          1. Xandrmoro
                            09.08.2017 15:20
                            +2

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


                1. DistortNeo
                  06.08.2017 19:23

                  По сути, мы перещаем бутылочное горлышко рендеринга из серверных процессов на клиента

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


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


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


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

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


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



                  1. funca
                    06.08.2017 22:07
                    -1

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


                  1. pnovikov Автор
                    06.08.2017 22:08

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


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

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


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

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


                    1. staticlab
                      06.08.2017 22:15

                      А что думаете насчёт связки изоморфное фронт-приложение + API-бэкенд?


                      1. pnovikov Автор
                        06.08.2017 22:24

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


                        1. staticlab
                          06.08.2017 22:26

                          Ждать webassembly, чтобы изоморфный сервер можно было поднять на другом языке?


                          1. pnovikov Автор
                            06.08.2017 22:29

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


                            1. staticlab
                              06.08.2017 22:33

                              А как изоморфируете на клиент? Транслируете C# в JS?


                              1. pnovikov Автор
                                06.08.2017 22:37

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


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


                                1. staticlab
                                  06.08.2017 22:44

                                  И это будет работать в общем случае, или потребуется какое-то допиливание/перепиливание существующих скриптов, шаблонов и компонентов?


                                  (Я имею в виду некий иной абстрактный .NET MVC-проект)


                                  1. pnovikov Автор
                                    06.08.2017 22:47

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


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


                                    1. staticlab
                                      06.08.2017 23:12

                                      Ну, как бы клиентские скрипты должны быть всё-таки рассчитаны принять от сервера не "базовый" HTML для расширения, а уже готовую вёрстку, подтянув её в свой VDOM, как это делает Реакт.


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


                                      Как я понял, в вашем случае при переходе на новую страницу будет сделана попытка обновить разметку на основе имеющегося VDOM, но что при этом будет с теми блоками, которые были изменены внешними клиентскими скриптами в обход VDOM, и что будет с уже запущенными скриптами первой страницы? Получается, что для корректной работы клиентские скрипты должны быть либо написаны с использованием некоей специфической библиотеки, либо быть сынтегрированы с C#-кодом для корректной инициализации и деинициализации (как это делается при использовании компонентов jQuery в компонентах React)?


                                      1. pnovikov Автор
                                        06.08.2017 23:25

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


                                    1. funca
                                      06.08.2017 23:16

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

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

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

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


                                      1. pnovikov Автор
                                        06.08.2017 23:25

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


                                        1. funca
                                          06.08.2017 23:30

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


                                          1. pnovikov Автор
                                            06.08.2017 23:31

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


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


                                1. kekekeks
                                  07.08.2017 10:45
                                  +1

                                  я уже почти собрал движок для изоморфного рендеринга на C#

                                  https://youtu.be/MiLAE6HMr10?t=1939 — к сведению-


                                  1. pnovikov Автор
                                    07.08.2017 11:18

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


                    1. DistortNeo
                      06.08.2017 23:00

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

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


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

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


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

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


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

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


                      1. pnovikov Автор
                        06.08.2017 23:13

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


                        1. DistortNeo
                          06.08.2017 23:45

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

                          Я тоже.


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

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


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

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


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

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


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


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


                          1. pnovikov Автор
                            06.08.2017 23:54

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

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


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

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


                            1. DistortNeo
                              07.08.2017 00:31

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

                              И это хорошо.


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

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


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

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


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


                              1. pnovikov Автор
                                07.08.2017 00:35

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


                              1. mayorovp
                                07.08.2017 07:40
                                +2

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

                                А можно пруф?


                                1. pnovikov Автор
                                  07.08.2017 09:30

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


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


                                  1. mayorovp
                                    07.08.2017 09:32

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


                                    1. pnovikov Автор
                                      07.08.2017 09:39

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


                                      1. franzose
                                        07.08.2017 09:43

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


                                1. DistortNeo
                                  07.08.2017 11:23
                                  +1

                                  А можно пруф?

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


                                  1. pnovikov Автор
                                    07.08.2017 11:40

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


                                    1. DistortNeo
                                      07.08.2017 12:01

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


                                      1. pnovikov Автор
                                        07.08.2017 12:02

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


                                  1. mayorovp
                                    07.08.2017 14:21

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


                1. grossws
                  07.08.2017 00:26
                  -2

                  Ведь все пользуются однопоточным 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 процессы и потоки технически отличаются не столь сильно.


                  1. mayorovp
                    07.08.2017 07:42

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


                    1. grossws
                      07.08.2017 07:47
                      +1

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


            1. serg_p
              06.08.2017 13:19
              +1

              На Луркморе упомянуты и каталагизированны_усе :)


            1. 0xd34df00d
              06.08.2017 19:32
              +2

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

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

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

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


              1. Antelle
                06.08.2017 19:49

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


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

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


                1. 0xd34df00d
                  06.08.2017 19:58
                  +4

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

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

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

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

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

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

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


          1. Keyten
            06.08.2017 16:54
            +1

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


        1. slonopotamus
          06.08.2017 12:58
          +5

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


          1. pnovikov Автор
            06.08.2017 13:00
            +5

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


            1. grossws
              07.08.2017 00:49
              +1

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


              1. pnovikov Автор
                07.08.2017 00:53
                +1

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


                1. grossws
                  07.08.2017 03:14
                  +1

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


      1. Mycolaos
        06.08.2017 12:13

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


        Так нужно было и писать о фанатиках, а не о технологии.


    1. dplsoft
      07.08.2017 00:27
      +6

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

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

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

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

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

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

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

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

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

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

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


      1. DistortNeo
        07.08.2017 03:32
        +1

        событийно ориентированный стиль разработки прекрасен.

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


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


        1. PsyHaSTe
          07.08.2017 18:56

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


          1. dplsoft
            07.08.2017 22:14

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

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

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

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


      1. PsyHaSTe
        07.08.2017 18:58
        +2

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


        1. dplsoft
          08.08.2017 11:57
          -3

          Правильнее было тогда бы наверное скорее обернуть через 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;
          }
          

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

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

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


          1. INC_R
            08.08.2017 12:37
            +3

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


            serial->waitForBytes( 18, 5000)

            на


            await serial->waitForBytesAsync(18, 5000)

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


            1. dplsoft
              08.08.2017 14:42
              -3

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

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

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

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

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

              А «асинхронный» вызов, не приостанавливает выполнение кода в точке вызова в ожидании ответа, а сразу преходит к следующей инструкции.


              1. INC_R
                08.08.2017 14:55
                +5

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

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


                1. vintage
                  09.08.2017 13:49
                  -2

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


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


                  1. INC_R
                    09.08.2017 14:36
                    +1

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

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


                    1. vintage
                      09.08.2017 14:51
                      -1

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


                      1. INC_R
                        09.08.2017 15:00

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


                        1. vintage
                          09.08.2017 15:17
                          -2

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


                          1. DistortNeo
                            09.08.2017 15:24

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


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


                            1. vintage
                              09.08.2017 15:41

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


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


                          1. INC_R
                            09.08.2017 15:37

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


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


                            1. vintage
                              09.08.2017 15:54

                              Код синхронный, когда исполнение идёт последовательно с ожиданием завершения подзадач. Используете ли вы для синхронизации 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));


                              1. INC_R
                                09.08.2017 16:09

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

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


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


                                1. vintage
                                  09.08.2017 16:27
                                  -3

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

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


                                  1. INC_R
                                    09.08.2017 16:57
                                    +1

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


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

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


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


                                    1. vintage
                                      09.08.2017 17:18
                                      -1

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


                                      1. INC_R
                                        09.08.2017 17:33
                                        +2

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


                                        1. vintage
                                          09.08.2017 17:44
                                          -8

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


                                          1. PsyHaSTe
                                            09.08.2017 17:51
                                            +5

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


                                          1. INC_R
                                            09.08.2017 17:53
                                            +2

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


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


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


                                            1. vintage
                                              09.08.2017 18:01
                                              -1

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


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


                                          1. DistortNeo
                                            09.08.2017 17:55
                                            +1

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

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


                                    1. dplsoft
                                      09.08.2017 17:50

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

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

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

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

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

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

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


                                      1. PsyHaSTe
                                        09.08.2017 17:53
                                        +1

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


                                        1. dplsoft
                                          11.08.2017 01:51
                                          -4

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

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

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

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

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

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

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

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

                                          на мисте, кстати, очень правильная фраза есть:

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

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

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

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

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

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

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

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

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

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

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

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


                                          1. PsyHaSTe
                                            11.08.2017 03:49
                                            +5

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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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

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


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


                                            1. dplsoft
                                              11.08.2017 09:17
                                              -6

                                              по порядку ))

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

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

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

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

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

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

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

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

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

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

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

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

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

                                              на остальную «лапшу» отвечу попозже.


                                              1. mayorovp
                                                11.08.2017 11:03
                                                +5

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

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


                                              1. grossws
                                                11.08.2017 11:45
                                                +5

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

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


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


                                              1. INC_R
                                                11.08.2017 11:55
                                                +6

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

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


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

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


                                                1. dplsoft
                                                  11.08.2017 13:08
                                                  -4

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

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

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


                                                  1. INC_R
                                                    11.08.2017 13:44
                                                    +5

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

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


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

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


                                                  1. PsyHaSTe
                                                    11.08.2017 15:11

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


                                                  1. mayorovp
                                                    11.08.2017 15:12

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


                                                1. dplsoft
                                                  11.08.2017 13:34
                                                  -3

                                                  Во-вторых, «волшебным образом» он это делает потому, что это по сути и есть 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 потока.

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


                                                  1. dplsoft
                                                    11.08.2017 13:40

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

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

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

                                                    от такой реализации — исходного ли «синхронность» исходного кода?


                                                  1. INC_R
                                                    11.08.2017 14:01
                                                    +3

                                                    вот как раз в документации то, ничего про 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ах мозг можно сломать.


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

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


                                      1. DistortNeo
                                        09.08.2017 17:59

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


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


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


                                        1. PsyHaSTe
                                          09.08.2017 18:04
                                          -1

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


                                          1. vintage
                                            09.08.2017 18:07

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


                                            1. pnovikov Автор
                                              09.08.2017 18:12
                                              +4

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


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


                                            1. DistortNeo
                                              09.08.2017 18:21
                                              +1

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

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

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

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

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


                                            1. Nakosika
                                              09.08.2017 18:26
                                              -1

                                              Я бы лучше поделил код на «последовательный» и «не последовательный».

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

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


                                              1. Nakosika
                                                09.08.2017 18:32

                                                Вернее, «синхронизация — это способ сделать параллельный код последовательным»


                                                1. pnovikov Автор
                                                  09.08.2017 18:40

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


                                      1. INC_R
                                        09.08.2017 18:06
                                        +3

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

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


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

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


                                1. dplsoft
                                  09.08.2017 17:34

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

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

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

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

                                  int main()
                                  {   ...
                                      serial->setOnByteIncomeCallBack(&onByteInome);
                                      serial->writeBytes(....);
                                  }
                                  
                                  int onByteInome(byte bytes[])
                                  {     ...
                                  }
                                  


                                  1. INC_R
                                    09.08.2017 17:42
                                    +2

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

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


                                    1. vintage
                                      09.08.2017 17:47
                                      -4

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


                                      1. PsyHaSTe
                                        09.08.2017 17:50

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


                                        1. vintage
                                          09.08.2017 18:06
                                          -4

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


                                          1. PsyHaSTe
                                            09.08.2017 18:06
                                            +4

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


                                          1. INC_R
                                            09.08.2017 18:12
                                            +1

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



                                            1. pnovikov Автор
                                              09.08.2017 18:29

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


                                              1. INC_R
                                                09.08.2017 18:36
                                                +1

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


                                              1. DistortNeo
                                                09.08.2017 18:38
                                                +1

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

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


                                                1. pnovikov Автор
                                                  09.08.2017 18:38

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


                                          1. DistortNeo
                                            09.08.2017 18:23
                                            +4

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

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


                                      1. DistortNeo
                                        09.08.2017 18:00
                                        +3

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


                                        1. vintage
                                          09.08.2017 18:03
                                          -2

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


                                          1. PsyHaSTe
                                            09.08.2017 18:06
                                            +1

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


                                            1. vintage
                                              09.08.2017 18:09
                                              -3

                                              Синхронный.


                                          1. DistortNeo
                                            09.08.2017 18:28

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

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


                                  1. PsyHaSTe
                                    09.08.2017 17:49
                                    +1

                                    Внезапно


                                    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


                                  1. 0xd34df00d
                                    09.08.2017 21:26

                                    О, 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);
                                    

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


                                    1. mayorovp
                                      10.08.2017 09:38

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


                                      1. 0xd34df00d
                                        10.08.2017 20:16

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

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


                  1. senia
                    09.08.2017 14:42
                    +3

                    Я бы хотел разобраться в вашей терминологии:
                    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)
                    }
                    


                    1. vintage
                      09.08.2017 15:01
                      -2

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


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


                      1. senia
                        09.08.2017 15:10
                        +3

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


                        1. vintage
                          09.08.2017 15:20
                          -3

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


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


                          1. DistortNeo
                            09.08.2017 15:27
                            +2

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


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


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

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


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


                            1. vintage
                              09.08.2017 15:58

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


                              1. senia
                                09.08.2017 16:07
                                +2

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


                                1. dplsoft
                                  09.08.2017 18:23
                                  -1

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

                                  Исходно, как я помню, мы говорили именно о логике исходного кода, и о том, что именно «синхронный» код позволяет описывать многие вещи проще и быстрее, чем событийно-ориентированный, асинхронный подход, потроенный на обработчиках и call-back.

                                  и так:

                                  Исходный код с использвоанием await — синхронный.
                                  Байт-код/машинный код может быть каким угодно. И это не отменяет синхронности исходного кода.

                                  Или вот для аналогии ещё пример: если я написал синхронные обертки над асинхронными функциями с call-back — то та часть исходного кода, которая использует обертки — синхронная. Вне зависимости от того, что внутри обертки — асинхронные функции.

                                  или вот ещё загадка:
                                  У меня есть абстрактный класс с одной функцией waitForBytes() которая не должна отдавать управлениедо момента своего окончания. Далее, я реализовал интерфейс в 2-х потомках — в одном я использвал синхронной функции, а в другом — на синхронных функциях и на асинхронных callback.

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


                                  1. INC_R
                                    09.08.2017 18:32
                                    +2

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


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

                                    Нет, они одинаковые. Трансформации, компиляция и все прочее не может менять логику.


                                    1. dplsoft
                                      09.08.2017 20:04
                                      +1

                                      Нет, они одинаковые. Трансформации, компиляция и все прочее не может менять логику.

                                      Вы о какой логике говорите? Логика бывает разного уровня.
                                      Если о логике и поведении описанном в коде высокого уровня — то оно сохраняется.

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

                                      Более того, логика работы исходного кода и логика работы машинного кода — они обязаны быть разными. Но обязаны реализовывать одинаковое поведение.

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

                                      Сохраняется поведение, но даже не всегда детали логики исходного кода.

                                      Посмотрите мой ответ https://habrahabr.ru/post/334964/#comment_10352244
                                      и скажите — оптимизирущие компиляторы — они какую логику меняют? и что они вообще меняют?

                                      ну на другой вопрос про разную реализацию await- тоже хотелось бы услышать ответ. спасибо )


                                  1. PsyHaSTe
                                    09.08.2017 18:50
                                    +1

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

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


                                    1. dplsoft
                                      09.08.2017 19:44
                                      +1

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

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

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

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

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

                                      А другой компилятор, может легко сделать не коллбеки, а 3 потомка и семафоры. Ведь согласитесь, await можно легко «сделать на семаформах».

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

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

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


                                      1. PsyHaSTe
                                        09.08.2017 23:14

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


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

                                        Если в терминах маш. команд или байт кода цикл является джампом на лейбл начала цикла, логика цикла от этого не меняется


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

                                        Я работаю не с байткодом, а с ЯП. Что под капотом — не важно. Это ведь ваше собственное утверждение :)


                                        1. dplsoft
                                          11.08.2017 02:52
                                          -2

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

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

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

                                          или классифицируйте в терминах вашей вселенной, что именно делает с кодом оптимизирующий компилятор, и какую логику (или что именно?!) он меняет?


                                          1. PsyHaSTe
                                            11.08.2017 03:54
                                            +1

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

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


                                            1. dplsoft
                                              11.08.2017 08:36
                                              -2

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

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

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

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

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


                                              1. PsyHaSTe
                                                11.08.2017 12:15
                                                +3

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

                                                От жонглирования синонимами ничего не меняется. Если для меня нет способа найти разницу между двумя представлениями, значит этой разницы нет. Бритва Оккама во все поля.


                                                1. dplsoft
                                                  11.08.2017 13:46
                                                  -2

                                                  От жонглирования синонимами ничего не меняется. Если для меня нет способа найти разницу между двумя представлениями, значит этой разницы нет. Бритва Оккама во все поля.

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

                                                  Так какую логику он меняет — можете описать? не уходите от ответа.


                                                  1. PsyHaSTe
                                                    11.08.2017 15:10
                                                    +2

                                                    Конечно не меняет, по определению компилятора.


                                                    Compiler optimization is generally implemented using a sequence of optimizing transformations, algorithms which take a program and transform it to produce a semantically equivalent output program that uses fewer resources.

                                                    Если читали книгу дракона (ну или вообще что-то по теме), то "семантика" это и есть логика программы. И по определению он её менять не имеет права.


                          1. senia
                            09.08.2017 15:28
                            +4

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


                            1. serg_p
                              09.08.2017 16:00

                              Смесь предметной реализации и обобщающих метафор( п.1 Требующих раскрытия в общем. П.2 И отдельно с реализацией). Термин вообще не привязан к компьютеру.


                    1. DistortNeo
                      09.08.2017 15:02

                      1. Да, код асинхронный.
                      2. Я не очень понимаю синтаксиса Scala, но если person имеет тип Future[Person], то мы не можем явно обращаться к person.id, будет просто ошибка.
                      3. Вариация кода 1, переписанная с использованием async/await.


                      1. senia
                        09.08.2017 15:12

                        Да все 3 примера делают абсолютно одно и то же (во втором считайте, что каждая стрелка — flatMap). Мне просто интересно как будет обосновываться не асинхронность первого примера или разный ответ для каких-либо 2 из них.


                  1. PsyHaSTe
                    09.08.2017 14:42
                    +4

                    await может вообще не создавать и не блокировать ни одного потока в принципе. Нужно просто осознать истину: никакого потока нет.


                    1. vintage
                      09.08.2017 14:54

                      Он блокирует поток исполнения задачи, который flow, а не thread.


                      1. mayorovp
                        09.08.2017 14:58
                        +2

                        Вот только синхронность/асинхронность определяется в терминах thread, а не flow.


                        1. vintage
                          09.08.2017 15:06
                          +1

                          Раз вы не любите ходить по ссылкам, то процитирую:


                          Asynchrony, in computer programming, refers to the occurrence of events independently of the main program flow and ways to deal with such events.


                          1. mayorovp
                            09.08.2017 15:09

                            Ну перепутали, бывает.


                          1. DistortNeo
                            09.08.2017 15:19
                            +2

                            Да, await блокирует flow исполнения задачи, ну и что? Он блокирует flow задачи, а не вызвавшего задачу кода (main program).


                            P.S. Вот мне интересно, почему люди считают Википедию истиной в последней инстанции?


                            1. vintage
                              09.08.2017 15:28

                              main program — такая же задача, как и все остальные.


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


                              1. DistortNeo
                                09.08.2017 15:48

                                main program — такая же задача, как и все остальные.

                                Ну и, собственно, что? Процесс, запустивший другой процесс, продолжит работу, не дожидаясь завершения вызванного процесса.


                                Или попробовать найти хоть одно различие между "task", "fiber" и "thread".

                                Task вообще никакого отношения к fiber и thread не имеет. Это просто объект (языка программирования), позволяющий отслеживать выполнение события и задавать продолжение — код, который будет выполняться по завершении задачи.


                                Ну а между Thread и Fiber основное различие только в том, что в первом случае планировщиком является операционная система, а Fiber — пользовательский код.


                                Теперь давайте вашу версию.


                                1. vintage
                                  09.08.2017 16:25
                                  -1

                                  Процесс, запустивший другой процесс, продолжит работу, не дожидаясь завершения вызванного процесса.

                                  А может и подождать, через await thread.join.


                                  Task вообще никакого отношения к fiber и thread не имеет. Это просто объект (языка программирования), позволяющий отслеживать выполнение события и задавать продолжение — код, который будет выполняться по завершении задачи.

                                  Я вам по секрету скажу fiber и thread — тоже не более, чем объекты.


                      1. INC_R
                        09.08.2017 15:09
                        +1

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


                      1. PsyHaSTe
                        09.08.2017 15:09
                        +1

                        Асинхронность, которая не блокирует flow — это fire and forget, частный случай "обычной" асинхронности, где мы после выполнения задачи все-таки хотим что-то сделать: узнать об этом, лампочкой помигать, что данные отправились и все такое.


              1. DistortNeo
                08.08.2017 15:31

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

                Вот серьёзно, я не очень понял вашу мысль. Можете пояснить?


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


                То есть асинхронное программирование — это просто хорошо завуалированное событийное программирование.


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


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

                То, что в Java для этого используют yield, означает только то, что в языке нет конструкции await, и поэтому приходится использовать не предназначенные для этого механизмы. Собственно, в C# до появления await тоже так делали.


                1. mayorovp
                  08.08.2017 15:37
                  -1

                  То, что в Java для этого используют yield, означает только то, что в языке нет конструкции await, и поэтому приходится использовать не предназначенные для этого механизмы. Собственно, в C# до появления await тоже так делали.

                  Вы не поняли, там речь шла о операции yield для потока. Это почти то же самое что Thread.Sleep(1) в C#. То есть жуткий костыль, которому вообще не место в стандартной библиотеке...


                  1. DistortNeo
                    08.08.2017 15:42

                    Вы не поняли, там речь шла о операции yield для потока. Это почти то же самое что Thread.Sleep(1) в C#. То есть жуткий костыль, которому вообще не место в стандартной библиотеке...

                    Ой...


              1. mayorovp
                08.08.2017 15:34

                Асинхронная функция отличается от синхронной тем, что она возвращает управление почти сразу же. И async/await — это именно что способ написания асинхронных функций.


                1. dplsoft
                  08.08.2017 23:31
                  -3

                  Асинхронная функция отличается от синхронной тем, что она возвращает управление почти сразу же.

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

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

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

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

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

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


                  1. INC_R
                    08.08.2017 23:37
                    +1

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


                    var receiveTask = client.ReceiveDataAsync(); // началось получение данных в фоне
                    CalculateSomething(); // долго считаем что-то, пока параллельно получаются данные
                    var data = await receiveTask; // досчитали все, что хотели, теперь ждем получения данных

                    Это все еще синхронно, ведь await все делает синхронным, верно?


                    1. vintage
                      09.08.2017 13:57
                      -7

                      Я вам больше скажу, в нормальных языках вы можете написать так:


                      var receiveTask = client.ReceiveData(); // началось получение данных в фоне
                      CalculateSomething(); // долго считаем что-то, пока параллельно получаются данные
                      var data = receiveTask; // досчитали все, что хотели, теперь ждем получения данных

                      await — лишь визуальный шум для синхронного кода.


                      1. mayorovp
                        09.08.2017 14:26
                        +3

                        Это что за "нормальный язык" такой? И как транслятор понимает где ждать, а где не ждать? Неужели смотрит на суффикс имени переменной? :-)


                        1. vintage
                          09.08.2017 16:36

                          Транслятор и не знает. ReceiveData возвращает прокси, который при попытке доступа к содержимому останавливает текущую задачу в ожидании прихода данных. Данный код на NodeJS + node-fibers: https://github.com/nin-jin/node-jin#sod


                          1. mayorovp
                            09.08.2017 16:40
                            +2

                            Тогда вот в этой строчке комментарий не соответствует реальности:


                            var data = receiveTask; // досчитали все, что хотели, теперь ждем получения данных


                            1. vintage
                              09.08.2017 16:54
                              -3

                              Ага, ну я не стал добавлять дополнительную строку использования data по назначению.


                              1. 0xd34df00d
                                09.08.2017 22:26
                                +1

                                У вас в D Хиндли-Милнера завезли, что ли, что он может вывести тип переменной по её дальнейшему использованию?


                                1. vintage
                                  10.08.2017 01:40

                                  А зачем тут оно? Достаточно прокси-подтипа.


                                  1. 0xd34df00d
                                    10.08.2017 01:53

                                    А каким будет тип, выведенный для data в вышеупомянутой строке?


                                    1. vintage
                                      10.08.2017 09:06

                                      Например Proxy!Json.


                                      1. PsyHaSTe
                                        10.08.2017 12:09

                                        То есть вы пишете var a = b, при этом у a не тот тип, что у b? И как он это выводит, интересно?


                                        1. vintage
                                          10.08.2017 13:52

                                          Да нет, какой тип вернули, такой и записали. receiveTask вернёт Proxy!Json, который является подтипом Json, но с дополнительным поведением по синхронизации.


                                          1. PsyHaSTe
                                            10.08.2017 18:43
                                            +1

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


                                          1. 0xd34df00d
                                            10.08.2017 20:16

                                            Тогда зачем что-то там переприсваивать?


                                            1. vintage
                                              10.08.2017 23:03

                                              Незачем.


                                              1. 0xd34df00d
                                                11.08.2017 02:28

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


                      1. INC_R
                        09.08.2017 14:30
                        +2

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


                        1. vintage
                          09.08.2017 16:53
                          -1

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

                          Любой язык, поддерживающий stackfull coroutines: D, Go, JS (node-fibers) и другие.


                          что мне делать, если нужно проверить, завершилась ли операция, а не ждать результата

                          Используйте флаги.


                          если я хочу дождаться результата и никак не использовать его

                          А зачем вам дожидаться результата, если он вас не интересует? И наоборот, если это не "запрос", а "команда", то зачем возвращать прокси, вместо немедленной синхронизации?


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

                          А зачем понимать на высоком уровне детали внутренней реализации метода ReceiveData? Это его дело, делать ли запрос к серверу, или сразу взять данные из кеша. А у вас — простой интерфейс.


                          1. DistortNeo
                            09.08.2017 17:07
                            +2

                            Любой язык, поддерживающий stackfull coroutines: D, Go, JS (node-fibers) и другие.

                            А если я не хочу использовать stackfull coroutines? Например, есть >10000 кооперативных задач, и каждой выделять стек — расточительство. Чем вам функции-генераторы не угодили?


                            Используйте флаги.

                            Так и до busy wait недалеко.


                            1. vintage
                              09.08.2017 17:21

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


                              1. DistortNeo
                                09.08.2017 18:08
                                +1

                                > Откуда у вас столько задач?

                                А вы не слышали про серверы, которые держат по 10к соединений?

                                > Впрочем, ну потратите вы мегабайт 50 на все их стеки — это капля в море.

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


                                1. vintage
                                  09.08.2017 18:22
                                  -2

                                  А вы слышали про сервера с десятками гигабайт оперативки?


                                  В стеке всё-равно зачастую одни ссылки на объекты в куче, так что 5 кило — это даже много. Кроме того, Go, например, умеет стеки ресайзить.


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


                          1. INC_R
                            09.08.2017 17:10
                            +3

                            Используйте флаги.

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


                            А зачем вам дожидаться результата, если он вас не интересует?

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


                            А зачем понимать на высоком уровне детали внутренней реализации метода ReceiveData?

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


                            public async Task<int> GetCurrentCounterAndIncrement()
                            {
                              var counter = await client.GetCounterAsync();
                              client.IncrementCounter();
                              return counter;
                            }

                            А теперь напишу по-вашему:


                            public int GetCurrentCounterAndIncrement()
                            {
                              var counter = client.GetCounter();
                              client.IncrementCounter();
                              return counter;
                            }

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


                            1. vintage
                              09.08.2017 17:30

                              Да нет, по моему будет так:


                              public int GetCurrentCounterAndIncrement()
                              {
                                var counter = client.GetCounter();
                                client.SetCounter( counter + 1 )
                                return counter;
                              }

                              Ничего, что в этом случае неочевидно

                              В соответствующем окружение вполне себе очевидно, что любая операция может:


                              1. быть асинхронной.
                              2. кинуть исключение.
                              3. записать что-то в лог.
                                ...


                              1. INC_R
                                09.08.2017 17:38

                                Да нет, по моему будет так:

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


                                В соответствующем окружение вполне себе очевидно, что любая операция может

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


                                1. vintage
                                  09.08.2017 17:50

                                  Ну а в вашем случае, будет возвращено не актуальное значение счётчика. Если оно использовалось для генерации идентификаторов, но привет коллизии.


                                  Пишите на ассемблере — будете точно всё знать.


                                  1. INC_R
                                    09.08.2017 18:19
                                    +1

                                    Ну а в вашем случае, будет возвращено не актуальное значение счётчика.

                                    А я сказал, что мне нужно актуальное, чтобы генерировать идентификаторы? Мне атомарность не нужна, если я хочу, скажем, показать пользователю число просмотров поста и одновременно увеличить его на 1. Если он увидит, что просмотров было 10, а не 17, ничего страшного не случится. А вот увеличение значения счетчика должно работать атомарно.


                                    Пишите на ассемблере — будете точно всё знать.

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


                                    1. vintage
                                      09.08.2017 18:33

                                      Вот и возвращайте ему актуальное значение счётчика:


                                      public int GetCurrentCounterAndIncrement()
                                      {
                                        return client.IncrementCounterAndGetIt();
                                      }

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


                                      1. INC_R
                                        09.08.2017 18:46
                                        +2

                                        Вот и возвращайте ему актуальное значение счётчика

                                        А если у меня нет такого метода? Если у меня есть только два метода: "получить текущее" и "увеличить на 1"?


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


                                        Если Операция А не дает мне понять, когда она там завершилась и завершилась ли вообще (ведь, по-вашему, мне не должно быть важно, как там внутри работает и когда), то что делать?


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

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


                                        1. PsyHaSTe
                                          09.08.2017 19:05

                                          del


                                      1. pnovikov Автор
                                        09.08.2017 18:57

                                        Слушайте, вы бы хотя бы бугуртили по поводу того, что встроенные коллекции в C# по умолчанию — не thread-safe, а так — слишком слабая иллюстрация протекающей абстракции. В таких случаях обычно про IncrementCounterAndGetIt пишут в документации thread-safe ли он или же нет. Если вдруг по какой-то причине в документации этого не сказано, то я бы подумал кто писал этот метод. Если его писали не клинические дебилы, то внутри наверняка Interlocked


                                      1. PsyHaSTe
                                        09.08.2017 19:07

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


                                        await drive.WriteBytes(bytes);
                                        Logger.Log("Write operation suceed");

                                        А что будет в вашем варианте?


                                        1. vintage
                                          09.08.2017 19:20
                                          -1

                                          !drive.WriteBytes(bytes);
                                          Logger.Log("Write operation suceed");


                                          1. INC_R
                                            09.08.2017 19:21
                                            +5

                                            Предельно очевидно. "Отрицание drive.WriteBytes()" Спасибо, заверните мне await.


                                          1. PsyHaSTe
                                            09.08.2017 19:26
                                            +2

                                            То есть вся разница в том, какой терм будет обозначать ожидание — ! или await? И я правильно понимаю, что ваш вариант тоже асинхронный?


                                            1. vintage
                                              09.08.2017 19:29
                                              -1

                                              Оба варианта синхронные неблокирующие.


                                              1. PsyHaSTe
                                                09.08.2017 19:35

                                                Я правильно понимаю, что:


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

                                                ?


                                                1. vintage
                                                  09.08.2017 20:20

                                                  Агась.


                                              1. DistortNeo
                                                09.08.2017 20:13
                                                +3

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


                                                1. vintage
                                                  09.08.2017 20:21

                                                  В данном коде не только операции ввода-вывода.


                                                  1. DistortNeo
                                                    09.08.2017 20:26

                                                    А какие ещё?


                                                    1. vintage
                                                      09.08.2017 20:45

                                                      Ещё запуск асинхронной задачи.


                      1. pnovikov Автор
                        10.08.2017 06:33

                        var data = receiveTask;


                        А если я просто хочу положить receiveTask в другую переменную — как мне быть в нормальных языках?


                        1. vintage
                          10.08.2017 09:11

                          Извиняюсь, что поленился и ввёл в заблуждение. Правильно так:


                          var data = client.ReceiveData(); // началось получение данных в фоне
                          CalculateSomething(); // долго считаем что-то, пока параллельно получаются данные
                          var name = data["name"]; // досчитали все, что хотели, теперь ждем получения данных


                          1. PsyHaSTe
                            10.08.2017 12:10
                            +2

                            Ну и тут у вас как раз блокировка, в отличие от асинхронного await, который ничего не блокирует.


                            1. vintage
                              10.08.2017 14:05
                              -1

                              Если вы про блокировку задачи, то await именно это и делает. Если про блокировку системной нити, то нет её тут.


                          1. pnovikov Автор
                            10.08.2017 12:27
                            +1

                            Ну т.е. вы предлагаете компилятору (интерпретатор тут написать ИМХО сложно) анализировать все последующие использования data на предмет "а когда оно разворачивается"? Попробуйте сами на досуге написать такой компилятор. Да еще и так, чтобы он не ждал на доступе к data, а честно ставил все, использует данные из data в continuation (уж простите за дотнетовскую терминологию). А если напишете — попробуйте использовать такой язык и не запутаться что в какой момент исполняется.


                            1. vintage
                              10.08.2017 13:57

                              Нет, я предлагаю компилятору не заниматься ерундой по созданию цепочек методов с потерей кол-стека. Волокна переключаются в рантайме по необходимости. Собственно я тут уже давал ссылку на свою реализацию этого дела на ноде: https://github.com/nin-jin/node-jin#sod


                              1. pnovikov Автор
                                10.08.2017 14:02

                                Ну и что у вас происходит в момент, когда чтение файла еще не завершилось, а пользователь уже требует контента?


                                1. vintage
                                  10.08.2017 14:11

                                  1. pnovikov Автор
                                    10.08.2017 14:25

                                    А концептуально, чтобы не вчитываться в ваш код?


                                    1. vintage
                                      10.08.2017 15:50

                                      Запоминается ссылка на текущее волокно. Текущее волокно уступает исполнение другим волокнам. Обработчик события прихода данных берёт сохранённое волокно и возобновляет его работу. Таким образом внешнее API у ReceiveData получается полностью синхронным. А под капотом — асинхронщина и автоматический параллеллизм.


                                      1. DistortNeo
                                        10.08.2017 16:55

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

                                        А вот когда мы переходим к колбэкам и синтаксическому сахару в виде async/await, получается уже асинхронщина.


                                        1. vintage
                                          10.08.2017 18:45

                                          У волокон под капотом те же самые колбэки. Например: https://github.com/laverdet/node-fibers#sleep


                                      1. pnovikov Автор
                                        10.08.2017 17:03
                                        +1

                                        Нуу… сомнительная и неоптимальная схема. Из вашего описания очевидно, что волокно, которое вызвало ReceiveData может заблокироваться на неопределенный промежуток времени, если оно посмело обратиться к результату раньше времени и уступило место cpu-bound волокну, которое считает там… какой-нибудь 100500й знак числа пи. Прикиньте как это отлаживать на производительность. Попытка найти боттлнек сожжет все нервы даже бывалому ассемблероиду. Если это трейдофф чтобы не писать await — то я пас.


                                        1. vintage
                                          10.08.2017 18:51

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


                                          1. DistortNeo
                                            10.08.2017 19:14
                                            +2

                                            Да не блокирует ничего await, поймите уже, наконец! Он просто задаёт продолжение — колбэк, который вызовется по завершении ожидаемой задачи.


                                          1. pnovikov Автор
                                            10.08.2017 20:13
                                            +1

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


                                            1. vintage
                                              10.08.2017 23:22

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


                                              1. Edison
                                                11.08.2017 05:39

                                                Что такое "волокно"? Можете писать понятными и принятыми терминами? Та же корутина, etc


                                                1. pnovikov Автор
                                                  11.08.2017 05:54

                                                  Имеется в виду функциональность, предоставленная вот этой библиотекой.


                                                  Да, это корутины.


                                                1. vintage
                                                  11.08.2017 09:11
                                                  +2

                                                  1. Edison
                                                    11.08.2017 10:40
                                                    -1

                                                    Я к тому, что я никогда не видел перевод термина fiber, волокно прям ухо режет. Лучше уже без перевода.


                                                    1. vintage
                                                      11.08.2017 13:22
                                                      +1

                                                      А вам ухо не режет, когда stream, thread и flow переводят одним словом "поток", а потом устраивают срачи из-за путаницы в терминах?


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


                                        1. DistortNeo
                                          10.08.2017 19:13
                                          +3

                                          Да в принципе, такая схема имеет право на жизнь. Просто CPU-bound волокон быть не должно. Все CPU-bound задачи должны в пул потоков отправляться, а запустившее такую задачу волокно йелдиться.

                                          Я сам работал с волокнами в С++, и даже в C#, и всё работало хорошо. Но один жирный минус перевешивал все плюсы, и минус этот — невозможность полноценной отладки волокон. Данные, лежащие в стэке другого волокна, попросту недоступны для отладчика, ну и всякие Step-ы по инструкциям не очень-то с переключениями между волокнами дружат.

                                          А потом появился await, и волокна я забыл как страшный сон.


                  1. DistortNeo
                    08.08.2017 23:54
                    +4

                    если быть точнее асинхронный вызов запускает выполнение потока инструкций в параллели

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


                    Явно запустить в параллели что-то вы можете через Task.Run(...), но к async/await это уже не имеет прямого отношения.


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

                    Изменение синхронности вызова функции? Бредятина какая-то. Начните с азов: почитайте, как устроена асинхронная функция, которая разворачивается компиляторов в целый класс, что такое await и раз раньше обходились без async/await, используя всякие там Task.ContinueWith.


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

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

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

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


                    Асинхронный код действительно выглядит как синхронный by design. Но от этого синхронным он не становится.


          1. PsyHaSTe
            08.08.2017 14:57
            +2

            Переписал (псевдокодовский микс C# и исходного кода):


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

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


            1. DistortNeo
              08.08.2017 15:41

              Посмотрите на следующий код и больше так не делайте:


              var originalTask = serial->waitForBytes(18);
              var resp=await Task.WhenAny(originalTask , Task.Delay(5000)); //ждем ответ принтера - минимум 8 байт, 5 секунд.
              if (resp != originalTask) return -100; // timeout
              if (sizeof resp.Result <8 ) return -101; // ответ не распознан

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


              Правильная реализация: с использованием CancellationToken, то есть:


                          byte[] res;
              
                          try
                          {
                              using (var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5)))
                                  res = await serial->waitForBytes(18, cts.Token);
                          }
                          catch (OperationCanceledException)
                          {
                              res = null;
                          }

              Ну и лучше это в функцию с try-catch обернуть для удобства.


              1. PsyHaSTe
                08.08.2017 15:51

                Это в случае, если у таски есть параметр с токеном. Если его нет, то упс. А вот если есть, то так конечно лучше.


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


                1. INC_R
                  08.08.2017 15:58
                  +2

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


                  1. PsyHaSTe
                    08.08.2017 16:07

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


                    1. DistortNeo
                      08.08.2017 17:53

                      Просто на моей практике почти все апишки не принимают "лишнего" параметра токена, из-за чего обработка таймаута происходит через такую обертку.

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


                      Поэтому отсутствие CancellationToken в таком API для меня — плохой признак.


                      1. PsyHaSTe
                        08.08.2017 19:29

                        Нет, просто например делают вызов через HttpClient, там вполне спокойно используется асинхронность. Сколько я видел использований этого класса? Миллион. Сколько из них принимали токен? ...


                        1. DistortNeo
                          08.08.2017 20:51

                          Плохой пример. В HttpClient как раз таки используется CancellationToken.


                          1. PsyHaSTe
                            08.08.2017 22:29

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


                            async string GetFooAsync(int id)
                            {
                               var url = GenerateUrl(id);
                               using (var client = new HttpClient())
                                  return await client.GetAsync(url);
                            }


                            1. DistortNeo
                              08.08.2017 22:37

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

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


                              1. PsyHaSTe
                                08.08.2017 22:42

                                Ну, я согласен, но разработка — командная работа, так что имеем что имеем :) На предыдущей работе я так и не смог объяснить, что в async-методе вызывать .Result не стоит. Так что по сравнению с этим определенно прогресс.


                                1. DistortNeo
                                  08.08.2017 23:14
                                  -1

                                  Почему же не стоит? Если Task.IsCompleted == true, то вполне можно.
                                  Вот здесь я, например, целых два раза использую .Result.


                                  Впрочем, мой код там таки содержит ошибку, и второй раз использование .Result недопустимо. Я забыл, что Task.WhenAny не бросает исключение, поэтому строчка должна выглядеть как:


                                  return cur.IsCompleted ? cur.Result : throw new TaskCanceledException();


                                  1. PsyHaSTe
                                    08.08.2017 23:28
                                    +2

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


                                    async string GetFooAsync(int id)
                                    {
                                       var url = GenerateUrlAsync(id).Result;
                                       using (var client = new HttpClient())
                                          return await client.GetAsync(url);
                                    }


                                    1. DistortNeo
                                      08.08.2017 23:37
                                      +2

                                      Кажется, понял мысль. Асинхронное программирование имеет иллюзию простоты: типа, написал await, и всё хорошо.


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


                                      1. PsyHaSTe
                                        09.08.2017 00:23
                                        +2

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


                                        1. pnovikov Автор
                                          09.08.2017 00:34

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


                                          Так что, in conclusion, не все решается асинками, не все решается нодой, не все пишется на Javascript-е :)


                                        1. DistortNeo
                                          09.08.2017 00:48

                                          Я придерживаюсь другого мнения: один раз примитивы написать можно и даже нужно. Велосипеды вообще писать полезно. Главное — не использовать их потом в продакшне.


                                    1. pnovikov Автор
                                      08.08.2017 23:44

                                      Вы еще про .ConfigureAwait человеку расскажите чтобы совсем крышу снесло :)


                                      1. DistortNeo
                                        09.08.2017 00:03
                                        +1

                                        Вы еще про .ConfigureAwait человеку расскажите чтобы совсем крышу снесло :)

                                        Я его, кстати, считаю костылём. Не от хорошей жизни его создали.


                                        В своих проектах я использую свой собственный планировщик, которому плевать на ConfigureAwait с высокой колокольни.


                                      1. PsyHaSTe
                                        09.08.2017 00:16

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


                                        Кстати полностью согласен, что это костыль, предпочел бы атрибут на уровне сборки (Fody в это умеет, к слову).


                                        1. mayorovp
                                          09.08.2017 10:44
                                          +1

                                          Самый простой способ избежать дедлоков — запретить вызывать Wait() или Result :-)


                                          Это и правда самое простое решение, и выглядит куда аккуратнее чем постоянные ConfigureAwait...


                                          1. PsyHaSTe
                                            09.08.2017 12:18

                                            Ну мы так и решили, потому что конфигурирование на "не нужен нам ваш контекст" выглядит как преждевременная оптимизация. Но тут возникает проблема с тем, что есть некоторые места, в которых можно написать только синхронно. А переписывание на асинхронное АПИ приводит к каскадным изменениям, кто конвертировал — тот знает. Но я согласен, что это нужно делать. Просто один коммит на 5000 файлов и который делается человекомесяц это весьма сурово, а постепенные изменения всегда приводят к тому, что где-то будет временная затычка в виде Result с потенциальным дедлоком.


                                            1. mayorovp
                                              09.08.2017 14:23

                                              Для таких случаев надо разработать решение без дедлоков и использовать его. Например, на основе 4го решения из этого ответа: https://ru.stackoverflow.com/a/514533/178779


                                              1. PsyHaSTe
                                                09.08.2017 14:49

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


                                            1. DistortNeo
                                              09.08.2017 14:30

                                              Попробуйте использовать AsyncRewriter для автоматизации этого процесса.


                1. DistortNeo
                  08.08.2017 16:10

                  Если его нет, то упс

                  Если нет, тогда нужно написать соответствующий враппер, либо не использовать внешний таймаут вообще. Как вариант — скофигурить таймаут через свойство ReadTimeout.


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


                  Мусора в первом варианте, как и ошибки, не будет, просто таска будет выполняться, я её результат никуда не уйдет.

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


                  1. PsyHaSTe
                    08.08.2017 16:16

                    Если нет, тогда нужно написать соответствующий враппер, либо не использовать внешний таймаут вообще. Как вариант — скофигурить таймаут через свойство ReadTimeout.

                    Как вы будете писать враппер, не зная внутренней структуры? Если знаете, то проблем никаких, но мы сейчас про функцию которая по сути с драйвером идет. И если там нет таймаута, то его никак не прикрутить, если только не дописывать этот драйвер самостоятельно. С другой стороны, что в примере он был, но почему я именно так написал я уже объяснил — практика.


                    А что будет происходить, когда одновременно будет несколько запросов на чтение?

                    Зависит от устройства.


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

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


                    1. DistortNeo
                      08.08.2017 16:34

                      Как вы будете писать враппер, не зная внутренней структуры?

                      Если ни один байт не должен быть потерян, то, например, так:


                          interface IDeviceInterface
                          {
                              Task<byte> ReadByte();
                          }
                      
                          class Device
                          {
                              readonly IDeviceInterface device;
                              Task<byte> cur;
                      
                              public Device(IDeviceInterface device)
                              {
                                  this.device = device;
                              }
                      
                              public async Task<byte> ReadByte(CancellationToken token)
                              {
                                  try
                                  {
                                      if (cur == null)
                                          cur = device.ReadByte();
                      
                                      if (cur.IsCompleted)
                                          return cur.Result;
                      
                                      var tcs = new TaskCompletionSource<bool>();
                      
                                      using (token.Register(tcs.SetCanceled))
                                          await Task.WhenAny(tcs.Task, cur);
                      
                                      return cur.Result;
                                  }
                                  finally
                                  {
                                      if (cur.IsCompleted)
                                          cur = null;
                                  }
                              }
                          }

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


                      1. PsyHaSTe
                        08.08.2017 16:41

                        Хороший пример, но ведь таска в таком случае точно так же остается висеть. От того, что мы заканселили таску мы прекратили её "типа", но при этом device.ReadByte() как читал, так и продолжает читать, и ничего про отмену не знает.


                        1. DistortNeo
                          08.08.2017 16:52
                          +1

                          Хороший пример, но ведь таска в таком случае точно так же остается висеть. От того, что мы заканселили таску мы прекратили её "типа", но при этом device.ReadByte() как читал, так и продолжает читать, и ничего про отмену не знает.

                          Да, таска остаётся висеть, но так и задумано, потому что при следующем чтении мы просто подцепляем старую таску, а не запустим новую.


                          Ну а если же задачу хочется принудительно остановить с потерей состояния — пишите метод Dispose.


                          1. PsyHaSTe
                            08.08.2017 16:58
                            -1

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


                            if (resp != originalTask)
                               originalTask.Dispose();

                            И да, гайдлайны не рекомендуют этого делать.


                            1. DistortNeo
                              08.08.2017 17:14

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

                              Это действие (Task.Dispose) бесполезно, так как мы так не прервём выполнение задачи. К тому же, я имел в виду device.Dispose() — чтоб сразу гильотиной действовать. Есть шанс, что задача в этом случае завершится с ошибкой.


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


                              1. PsyHaSTe
                                08.08.2017 17:38

                                В плане "не потерять таску и подобрать её, когда она упала" задумка классная, надо запомнить.


                                К тому же, я имел в виду device.Dispose() — чтоб сразу гильотиной действовать. Есть шанс, что задача в этом случае завершится с ошибкой.

                                Но ведь device не реализует IDisposable.


                                1. DistortNeo
                                  08.08.2017 17:45

                                  Ну так реализуйте
                                      interface IDeviceInterface: IDisposable
                                      {
                                          Task<byte> ReadByte();
                                      }
                                  
                                      sealed class Device
                                          : IDisposable
                                      {
                                          readonly IDeviceInterface device;
                                          Task<byte> cur;
                                  
                                          public Device(IDeviceInterface device)
                                          {
                                              this.device = device;
                                          }
                                  
                                          public async Task<byte> ReadByte(CancellationToken token)
                                          {
                                              try
                                              {
                                                  if (cur == null)
                                                      cur = device.ReadByte();
                                  
                                                  if (cur.IsCompleted)
                                                      return cur.Result;
                                  
                                                  var tcs = new TaskCompletionSource<bool>();
                                  
                                                  using (token.Register(tcs.SetCanceled))
                                                      await Task.WhenAny(tcs.Task, cur);
                                  
                                                  return cur.Result;
                                              }
                                              finally
                                              {
                                                  if (cur.IsCompleted)
                                                      cur = null;
                                              }
                                          }
                                  
                                          public void Dispose()
                                          {
                                              device.Dispose();
                                          }
                                      }


                                  1. PsyHaSTe
                                    08.08.2017 19:28

                                    Так речь шла о случае, когда он не реализует...


                                    1. DistortNeo
                                      08.08.2017 20:52

                                      Ну если не реализует, тогда остановить операцию не получится — очевидно же.


              1. mayorovp
                08.08.2017 15:56

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


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


                1. PsyHaSTe
                  08.08.2017 16:17

                  Событийной архитектуре трудно быть понятной, потому что там флоу нелинейный. Приведите пример что ли, в каком случае EAP будет понятнее, чем async/await флоу?


                  1. mayorovp
                    08.08.2017 16:26

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


                    Пример
                                 R:HDR @=======@ S:HDR             R:HDR[!=S:HDR]
                              +--------| START |-----+    +--------------------------------+
                              |        @=======@     |    |                                |
                             \|/                    \|/   |                                |
                         @==========@             @==========@ S:OPEN                      |
                    +----| HDR_RCVD |             | HDR_SENT |------+                      |
                    |    @==========@             @==========@      |      R:HDR[!=S:HDR]  |
                    |   S:HDR |                      | R:HDR        |    +-----------------+
                    |         +--------+      +------+              |    |                 |
                    |                 \|/    \|/                   \|/   |                 |
                    |                @==========@               +-----------+ S:CLOSE      |
                    |                | HDR_EXCH |               | OPEN_PIPE |----+         |
                    |                @==========@               +-----------+    |         |
                    |           R:OPEN |      | S:OPEN              | R:HDR      |         |
                    |         +--------+      +------+      +-------+            |         |
                    |        \|/                    \|/    \|/                  \|/        |
                    |   @===========@             @===========@ S:CLOSE       +---------+  |
                    |   | OPEN_RCVD |             | OPEN_SENT |-----+         | OC_PIPE |--+
                    |   @===========@             @===========@     |         +---------+  |
                    |  S:OPEN |                      | R:OPEN      \|/           | R:HDR   |
                    |         |       @========@     |          +------------+   |         |
                    |         +------>| OPENED |<----+          | CLOSE_PIPE |<--+         |
                    |                 @========@                +------------+             |
                    |           R:CLOSE |    | S:CLOSE              | R:OPEN               |
                    |         +---------+    +-------+              |                      |
                    |        \|/                    \|/             |                      |
                    |   @============@          @=============@     |                      |
                    |   | CLOSE_RCVD |          | CLOSE_SENT* |<----+                      |
                    |   @============@          @=============@                            |
                    | S:CLOSE |                      | R:CLOSE                             |
                    |         |         @=====@      |                                     |
                    |         +-------->| END |<-----+                                     |
                    |                   @=====@                                            |
                    |                     /|\                                              |
                    |    S:HDR[!=R:HDR]    |                R:HDR[!=S:HDR]                 |
                    +----------------------+-----------------------------------------------+


                    1. PsyHaSTe
                      08.08.2017 16:38
                      +1

                      Учитывая, что async/await как раз-таки и разворачивается в КА, не вижу проблем. Как раз в этом случае можно просто воротить if else и всю эту логику, а с событиями придется попотеть. Каждый ведь с каждым нужно связать, и все эти взаимосвязи уследить в таком случае представляется не очень простой задачей.


                      1. mayorovp
                        08.08.2017 16:42

                        Тот факт, что async/await разворачивается в КА никак не помогает писать код при заданном КА. Фактически, программисту нужно выполнить работу обратную той, которую делает компилятор.


                        А вот уследить за взаимосвязями — как раз нет проблем. Вот они все, на картинке.


                        1. PsyHaSTe
                          08.08.2017 16:51

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


                          В чем проблема по такой схеме написать что-то вроде:


                          var data = await port.SendDataAsync();
                          if (foo)
                             await OpenPipe();
                          else 
                             await OpenRcvd();

                          я, честно говоря, пока не понял.


                          1. mayorovp
                            08.08.2017 16:54

                            Не вижу в вашем коде обработки ситуации, когда половина данных была успешно отправлена, а потом наступил тайм-аут...


                    1. DistortNeo
                      08.08.2017 17:13

                      Посмотрите внимательно:


                      1. У вас в конечном автомате поток идёт сверху вниз без циклов. Вы никогда не возвращаетесь в предыдущее состояние. В этом случае плоский код с if-then-else может оказаться понятнее и проще в реализации.


                      2. Управляющие команды всегда идут в строгом порядке — HDR, OPEN, CLOSE. Почему бы это не использовать в коде? Можно, например, уменьшить количество состояний, если полностью от них не отказаться.


                1. DistortNeo
                  08.08.2017 16:18

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

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


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


                  P.S. В любом случае, оставлять за собой висящие таски — это, по меньшей мере, некрасиво.


            1. dplsoft
              08.08.2017 21:53
              -7

              Очевидно, что код полностью асинхронный

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

              Но то, что вы тут уже 5й пост устраиваете параолимпиаду по синхронным методам запуска асинхронных функции… и при этом заявляете что это «полностью асинхронный код»…

              Я потрясен. «Такое» я вижу впервые.
              Вы… в некотором роде, теперь, мой личный рекордсмен.
              В отдельной, особой номинации. Для «тяжеловесов».

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

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


              1. DistortNeo
                08.08.2017 22:35
                +1

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

                Ну и заявление! Именно детали реализации — это и есть самое важное в асинхронном программировании. Если конкретно вам на это плевать — это ваши проблемы.


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

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


                И представьте себе, при использовании C# обёртки придётся писать и для обычных синхронных функций.


                1. vintage
                  09.08.2017 14:12
                  -1

                  это и есть самое важное в асинхронном программировании

                  В конкурентном исполнении, а не асинхронном программировании.


                  Краткие определения для упорядочивания:


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


                  1. DistortNeo
                    09.08.2017 14:48

                    В конкурентном исполнении, а не асинхронном программировании.

                    Да, верно. Ну и ещё один момент: асинхронное программирование является синтаксическим сахаром для событийно-ориентированного программирования. Иногда это бывает очень удобно.


                    Параллелизм / конкурентность — это относится к исполнению, это, фактически, свойство планировщика задач


                    А вот асинхронность / событийность — это относится уже к программированию.


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


                    Но нюанс в том, что существующие планировщики IO-bound операции выполняют конкурентно, а для запуска CPU-bound задачи нужно планировщик явно об этом попросить (Task.Run, ConfigureAwait и т.д.). И именно эта деталь реализации является принципиально важной.


                    P.S. Я, кстати, пробовал писать велосипед с Fibers — получался чисто синхронный код с конкуретным выполнением.


                  1. serg_p
                    09.08.2017 15:34

                    Конкурентность никак не привязана к одному. Одним, многими. А бывает что и некому — по факту :)


              1. PsyHaSTe
                08.08.2017 22:39
                +1

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

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


                1. dplsoft
                  09.08.2017 01:25
                  -4

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

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

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

                  вот скажем, о каких потоках и блокировках, мы можем говорить, если мы попытаемся определить, скажем что такое синхроные и асинхронные вебсервисы?

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

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

                  Блокировка потока (который thread) «как признак асинхронности» — это частный случай.

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

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


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

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

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

                  вы не переходите на следующие инструкции пока не выполните операции чтения из компорта. иначе вся логика работы кода разрушится.

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

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

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


                  1. DistortNeo
                    09.08.2017 01:54
                    +4

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

                    В этом и заключается ваша ошибка. Вы никак не можете понять, что асинхронная функция вернёт результат (Task<>, Promise и т.д.) до того, как завершится её выполнение. Да, управление внутри функции не перейдёт на следующую после await строчку, ну и что с того? Вызвавший эту функцию код уже получит от функции результат.


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

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


                  1. 0xd34df00d
                    09.08.2017 01:59
                    +6

                    Такое чувство, будто у вас асинхронность не ради асинхронности, а ради боли, страданий и бойлерплейта.


                  1. PsyHaSTe
                    09.08.2017 02:01
                    +4

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

                    Это ровно то, как работает await :)


                    1. jetexe
                      09.08.2017 10:41
                      +4

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


                      1. PsyHaSTe
                        09.08.2017 12:26

                        Кстати да, я думал, что неплохо понимаю await, но этот пример неплохо иллюстрирует такое непонимание, в котором сам до недавних пор пребывал:


                        async Task Main()
                        {
                            var sw = Stopwatch.StartNew();
                            var a = Task.Delay(2000).ContinueWith(t => 0);
                            var b = Task.Delay(2000).ContinueWith(t => 0);
                            var aa = await a;
                            var bb = await b;
                            Console.WriteLine($"Result = {aa + bb}, Elapsed = {sw.Elapsed.TotalSeconds}"); // 2 секунды
                        }

                        Инлайним a и b:


                        async Task Main()
                        {
                            var sw = Stopwatch.StartNew();
                            var aa = await Task.Delay(2000).ContinueWith(t => 0);
                            var bb = await Task.Delay(2000).ContinueWith(t => 0);
                            Console.WriteLine($"Result = {aa + bb}, Elapsed = {sw.Elapsed.TotalSeconds}"); // 4 секунды
                        }

                        Я раньше думал, что await в том числе запускает асинхронную операцию, а создание фактически эквивалентно new Task(...), а на самом деле скорее Task.Run.


                1. vintage
                  09.08.2017 14:14
                  -2

                  setTimeout( ()=> console.log( 'one' ) , 10 )
                  console.log( 'two' )

                  Асинхронный блокирующий.


                  1. PsyHaSTe
                    09.08.2017 14:52
                    +3

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


                    1. vintage
                      09.08.2017 17:16

                      Оба кода на время своего исполнения блокируют поток, но исполняются асинхронно.


                      1. PsyHaSTe
                        09.08.2017 17:38
                        +2

                        Если в вашей терминологии "блокировать поток" == "выполнять в нем полезные операции", то будьте добры так заранее и говорить, потому что иначе возникает путаница. Потому что в общепринятой код


                        puts "hello world"

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


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


                        1. vintage
                          09.08.2017 17:55

                          console.log — блокирующий io.


                          1. PsyHaSTe
                            09.08.2017 18:55

                            console.log — это библиотечная функция, что она под капотом делает меня не касается. Функция является синхронной, если она возвращает объект, и асихнронной, если возвращает промиз. Что там под капотом — не моя зона ответственности. Асинхронность задается на уровне контракта — интерфейса — а не на уровне имплементации.


                            1. vintage
                              09.08.2017 19:24

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


                              1. DistortNeo
                                09.08.2017 20:25
                                +1

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

                                Неверно. Всё зависит именно от того, что находится под капотом, а не какой интерфейс предоставляет функция. Если результат синхронного (=блокирующего) вызова обернуть в Task, он сам вызов останется блокирующим, асинхронным он от этого не станет.

                                И наоборот, функции чтения типа stream.BeginRead, хотя и не возвращают Task<> и не позволяют использовать await, являются вполне себе асинхронными.


                                1. vintage
                                  09.08.2017 20:52
                                  -2

                                  Если результат синхронного (=блокирующего) вызова обернуть в Task, он сам вызов останется блокирующим, асинхронным он от этого не станет.

                                  В правильной реализации вообще-то станет.


                                  v = new Promise(resolve=>resolve(1))
                                  v.then(console.log)
                                  console.log(2)

                                  Выведет сначала 2, потом 1.


                              1. TimTowdy
                                10.08.2017 12:20
                                +3

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

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

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

                                Flies away.


                        1. DistortNeo
                          09.08.2017 18:35

                          > под блокировкой потока обычно имеют ввиду «усыпим тред, пока нас кто-то не разбудит».

                          Операционная система тоже может поток заблокировать, например, для выполнения операций ввода-вывода.

                          puts, console.log — это блокирующие операции. Как думаете, что будет, если вы перенаправите вывод, например, на дискету или в ком-порт с медленной скоростью?


                          1. PsyHaSTe
                            09.08.2017 19:00

                            Вызов console.log ничего не блокирует. Если блокируется внутри console.log — это, как я уже выше написал, не моя зона ответственности, и для меня этот метод полностью выглядит и ведет себя как обычный синхронный. Сколько там оно будет выполняться — beside the point, в определении синхронной операции нигде не сказано, какие временные рамки должны быть, чтобы она считалась таковой. Функция не может меняться от "синхронной" до "асинхронно" от того, посмотрели ли мы её реализацию или нет. Либо она синхронная — и тогда мы её просто вызываем и получаем результат, либо она асинхронная, и мы должны каким-либо образом подписаться на получение результата в будущем.


                            1. DistortNeo
                              09.08.2017 19:11
                              +2

                              > Вызов console.log ничего не блокирует. Если блокируется внутри console.log — это, как я уже выше написал, не моя зона ответственности, и для меня этот метод полностью выглядит и ведет себя как обычный синхронный.

                              Как это не ваша? Ещё как ваша. Если вы, например, загружаете веб-странцу, вам должно быть принципиально важно, как реализованы операции с сокетами. Просто об этом должно быть написано в документации.

                              Если после вызова функции она моментально возвращает управление, а в консоль данные пишутся в фоне, то она асинхронная (=неблокирующая). Ну а то, что не возвращается Task — считайте, что это fire-and-forget.

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


                              1. pnovikov Автор
                                09.08.2017 19:30
                                +1

                                На сколько я помню, в EntityFramework есть такой факап


                              1. PsyHaSTe
                                09.08.2017 19:32
                                -1

                                Как это не ваша? Ещё как ваша.

                                С чего это? Я вызываю функцию int foo = bar(). Я что-то блокирую? Или ответ зависит от того, как написана bar?


                                Если после вызова функции она моментально возвращает управление, а в консоль данные пишутся в фоне, то она асинхронная (=неблокирующая). Ну а то, что не возвращается Task — считайте, что это fire-and-forget.
                                
                                И наоборот, функция может возвращать Task, но вызывать внутри блокирующие операции и возвращать в самом конце Task.FromResult (программист просто поленился и поставил такую заглушку). От того, что она возвращает Task, асинхронной она не становится.

                                Так мы не про функцию работаем, а про работу с ней. await на Task.FromResult является асинхронной операцией, а вызов fire-and-forget в большинстве случаев говнокодом, потому что это как раз и есть "запустим асинхронно, но вызывающему коду не скажем". Но да, её вызов будет синхронным, потому что мы явно вызвали синхронную функцию, а что у нее внутри мы не знаем.


                                1. DistortNeo
                                  09.08.2017 20:20

                                  > Или ответ зависит от того, как написана bar?

                                  Да, зависит. Признак асинхронности — продолжение работы после возврата из функции.


                                  1. PsyHaSTe
                                    09.08.2017 23:59

                                    Да, зависит. Признак асинхронности — продолжение работы после возврата из функции.

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


                                    1. DistortNeo
                                      10.08.2017 00:41

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

                                      А что насчёт Stream.BeginRead или Socket.BeginReceive?


                                      1. PsyHaSTe
                                        10.08.2017 00:51

                                        Я же сказал "грубо говоря" :)


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


                                        1. DistortNeo
                                          10.08.2017 01:15

                                          Ну значит, всё верно. Если функция возвращает Task<>, то мы имеет право полагать, что функция действительно асинхронная, за исключением случаев, когда имеет место говнокод, например, функция объявлена как async, но внутри только синхронные операции или, о ужас, обращение к синхронному task.Result.


                                          1. pnovikov Автор
                                            10.08.2017 06:28
                                            -1

                                            return Task.FromResult(синхронная_операция)


                                            1. mayorovp
                                              10.08.2017 09:46
                                              -1

                                              Это и называется "говнокод". Если академичнее, то реализация функции не соответствует контракту.


                                              1. pnovikov Автор
                                                10.08.2017 11:56

                                                Я понима~ — я просто привел пример кода, который может лежать за якобы асинхронной функцией, ломая всю малину. Уберите минус :)


                                                1. PsyHaSTe
                                                  10.08.2017 12:06

                                                  Асинхронная функция — это не та, которая написана "асинхронно" (то есть нам не важно, что у неё в теле написано), а взаимодействие с которой происходит по асинхронному флоу — формирование задачи и ожидание её завершения. Что там под капотом — не важно, Task.FromResult, реальный запрос по сети/к устройству или еще что-то.


  1. PFight77
    06.08.2017 00:34
    -6

    Про AngularJS в 2017-том писать… Как буд-то статья была написана несколько лет назад.


    1. pnovikov Автор
      06.08.2017 00:43
      +1

      Официальный релиз AngularJS 2 состоялся 15 сентября 2016 года.


      1. PFight77
        06.08.2017 00:55
        -2

        AngularJS называют Angular 1. Тот что 2 называют просто Angular. Ну и вообще, Angular сейчас ловит хайпа куда меньше чем React. У Вас как бы наоборот.


        1. pnovikov Автор
          06.08.2017 00:57

          Буду знать


  1. fromgate
    06.08.2017 00:53

    А аналогичная градация для C#, Java и т.п. возможна? ;)


    1. pnovikov Автор
      06.08.2017 00:55

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


      1. igorch96
        06.08.2017 03:21
        +4

        Ну JavaScript постарше C# будет…


        1. pnovikov Автор
          06.08.2017 07:08
          +1

          Во времена, когда AJAX был неизвестным нововведением, а на горизонте маячил Web 2.0 — на C# не было лямбда-выражений и LINQ, но уже во всю писали мелкий банкинг и CRM-системы, а все холивары Java vs. C# уже отгремели канонадой. Так что формально JavaScript старше, но активно использовать и развивать его начали значительно позже.


          1. funca
            06.08.2017 16:13
            -1

            О, кажется началось)


  1. KasperGreen
    06.08.2017 02:00
    +1

    У меня 8 стадия похоже. Спасибо за статью. Пожалуй для меня уже поздно что-то менять :)


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


    Про многопоточность кстати всё не так плохо. Плохо конечно, но уже и прямой доступ к памяти есть, и кривоватая, но многопоточность на видяхах.


    Уверен, что если нужна числодробилка с миллионом операций в секунду лучше использовать хорошие низкоуровневые языки, а всякий скрипт подойдёт для быстрого написания тормозного прототипа, который можно переписать на труёвый язык. Ты же уже полюбил переписывать код по несколько раз, %Юзернейм%?


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


  1. KroTozeR
    06.08.2017 02:07
    +16

    Пишу на C/C++. За плечами опыт ВЕБ-программиста (ещё не Frontend), когда jQuery был модным. Сбежал обратно на C/C++ от ВЕБ-а именно чтобы не иметь дела с людьми, которых описывает автор. Не считаю их идиотами или безумными фанатиками, но… Видимо мне с ними не по пути.

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

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

    А глубинная причина лишь одна: аппаратная абстракция. Категорическое нежелание понимать машину. Плохо это? Хорошо? Да пусть пишут на этом языке, если он им так нравится) Только пусть не ведут «войну с неверными». Это как воевать за всеобщую автоматизацию, надеясь на вечную выработку электроэнергии.
    JavaScript — красивый язык. Он очень демократичный и лёгкий. Почти идеальный скриптовый язык, о чём и говорит его название.

    Я же предпочту знать, как работает ЭВМ, и насколько нелепыми кажутся со столь «низкого» уровня все эти танцы уверовавших в абсолютную многопоточность JS-программеров. Да-да, я знаю, как работает механизм её обеспечения, а потому знаю, что многопоточности нет. По крайней мере в рамках одной ЭВМ. Есть иллюзия.

    К слову сказать, сколь бы ни были противоречивы языки go и Rast, но всё же это — попытка поиска «золотой середины». Это всё вытекает из интересов бизнеса. Заметьте! Бизнеса, а не науки. Наука преследует порядок, а бизнесу он не нужен. Ему нужен заработок. Так что, терпите. Или изобретайте альтернативу. Есть же проекты вроде LLVM.


    1. Graff46
      06.08.2017 02:33
      +1

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


      1. igorch96
        06.08.2017 22:23

        del


    1. Scf
      06.08.2017 10:35
      +1

      Многопоточность в рамках одной ЭВМ очень даже есть. Многоядерные процессоры, устройства на общей шине, DMA...


      1. KroTozeR
        06.08.2017 10:49
        +9

        Так и полагал, что кто-то это напишет) Это не многопоточность, как ни странно. Ядра процессоров сидят на едином кэше третьего или второго уровня (у кого как), а доступ к этому кэшу — последователен для всех в порядке общей очереди. То же самое касается отдельных процессоров и ОЗУ. А то, что творится в рамках одного кристалла, это как исполнение макрокоманды. В общем, в этой ситуации многопоточность существует лишь на определённых уровнях приближения. Чтобы достичь настоящей многопоточности, нужно отказаться от архитектуры Фон-Неймана с её разделяемым доступом к памяти и устройствам.

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


        1. Scf
          06.08.2017 11:58
          +1

          Да, общая память как "выжившая" массовая архитектура. Что поделаешь — она наиболее удобна для программирования. Но у каждого ядра есть собственный кеш и в рамках него они работают независимо (почти). Алгоритм синхронизации кешей L1 весьма сложен, как и модель многопоточности процессора, см. memory fences и x86 fence instructions.


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


          1. KroTozeR
            06.08.2017 12:13
            +1

            DMA да. Это пример многопоточности. Вообще, сейчас у меня состоялся дискут с коллегой, не менее глубоко разбирающимся в архитектуре процессоров. Сошлись на том, многопоточность зависит от того, к чему она применяется. И всё зависит от того, как относиться к разделению общих ресурсов. Но даже так количество потоков не может быть больше суммарного количества логических ядер ЦП внутри ЭВМ.


            1. KostaArnorsky
              07.08.2017 18:44
              +1

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


        1. serg_p
          06.08.2017 13:24

          Для 4 и более процессорной- уже не актуально


    1. serg_p
      06.08.2017 13:22

      Хорошо описано в «законе дырявых абстракций» — проблема старее и глубже


  1. Strain
    06.08.2017 02:34
    -2

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

    — кривая реализация integer в JS ( для многих задач крайне критично )
    — в стрелочных функциях можно не писать return ( казалось бы ура ), но если там больше одной строки — без написания return спокойно возвращается undefined без всяких предупреждений
    — неявные преобразования очень коварны — никогда на 100% нет уверенности какой же тип возвращает данный колбэк из DOM элемента / React компонента ( onChange, onSubmit etc ) и непонятно можно ли с ним безопасно проводить арифметические действия

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


    1. farwayer
      06.08.2017 09:21
      +6

      кривая реализация integer в JS

      как может быть криво реализовано то, чего нету?)

      без написания return спокойно возвращается undefined без всяких предупреждений

      А почему функция без моего ведома должна что-то возвращать? Я понимаю, вы привыкли, что в функциональных языках так. Но JS — мультипарадигменный язык. Да, с однострочной лямбдой в 95% случаев мы хотим вернуть значение. С многострочной — уже не факт. А если принимающий код неправильно среагирует на это значение? Это не теория, а вполне конкретный случай факапа с coffeescript (в котором неявный return как раз).

      неявные преобразования очень коварны — никогда на 100% нет уверенности какой же тип возвращает данный колбэк

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

      я находился под воздействием свежего погружения в React-Native

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

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

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


      1. grossws
        07.08.2017 01:02
        +1

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

        Вы путаете динамическую и сильную/слабую (strong/weak typing) типизацию. Первая про то, что переменная обладает типом только в рантайме, а вторая касается автоматического приведения типов. Например, python — dynamic, но strong: тип переменной определяется в рантайме, но, скажем число не приводится к строке при попытке написать "1" + 2, оно просто даст по рукам exception'ом.


        1. TimTowdy
          07.08.2017 01:26

          Все верно, хотя умножение у последовательностей (в том числе строк) все же переопределено:

          >>> '3' * 5
          "33333"
          


          1. 0xd34df00d
            07.08.2017 08:07

            Ой, кажется, все отрицательные числа равны друг другу:

            >>> '3' * (-1) == '3' * (-2)
            True
            


            1. grossws
              07.08.2017 08:12

              Да, несколько странное поведение, но оно мне на enumerable/iterable вообще не очень нравится. Как и корявое a if b else c.


              1. 0xd34df00d
                07.08.2017 08:19

                Как для этого a if b else c грамматика написана и зачем именно так, мне не очень понятно.


                1. grossws
                  07.08.2017 08:26

                  Выражение a if b else c возвращает a при истинности b, иначе возвращает c. В общем вывернутый наизнанку тернарный оператор.


            1. oxidmod
              07.08.2017 09:07
              -3

              Что-то пошло не так.
              image

              зы. не фанат js


        1. farwayer
          07.08.2017 07:59

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


    1. Mycolaos
      06.08.2017 12:27

      Похоже у Вас уровень 4 — «Вы просто не понимаете сути», только на Вашем языке. Какой там у Вас язык?


      1. Strain
        06.08.2017 12:32

        Написано же — JavaScript, ES6


      1. Strain
        06.08.2017 12:40

        А так — много языков на которых пишу. Основной который приносит деньги — Elixir / Erlang. Также люблю Scala, Clojure, Haskell


    1. staticlab
      06.08.2017 12:36

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

      Смотрите, во-первых, там есть синтаксическое различие: выражение передаётся без фигурных скобок, а операторы — в фигурных скобках. Во-вторых, если вам так уж хочется автовозврата значения, то есть оператор do (но, stage 0)


    1. Keyten
      06.08.2017 17:00

      В любом туториале по стрелочным функциям (или туториале по особенностям ES6, где упоминаются стрелочные функции), в любом! — упоминается, что многострочные стрелочные функции требуют return.
      Кто вам виноват, что вы не потрудились прочитать базовых вещей?


      1. Strain
        06.08.2017 17:14
        +3

        Диагноз
        4 уровень: «Вы просто не понимаете сути»

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


        1. staticlab
          06.08.2017 22:20
          +2

          Кстати говоря, в Java точно так же есть стрелочные функции с фигурными скобками и без. Для первых нужен return. А вот варианта () => do { } там нет.


        1. Keyten
          06.08.2017 23:32
          +3

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

          … определённо говорит об удобстве, высокой продуманности...

          Подобное можно написать о любой фиче в любом языке. А уж в функциональных-то — тем более.
          А this в самом деле очень прост. Не говоря уже про return. Я даже описал всё это всего лишь двумя абзацами.

          Немного негодования по теме
          Если я приду в ваш язык и буду негодовать, что return где-то не требуется, что вы сделаете? Согласитесь «ну да, вот язык такой ужасный, сам мучаюсь» или пошлёте читать мануал?

          Если я приду в C++ и заявлю, что деление на h и cpp-файлы некошерно, и вообще никто в 2к17 не подключает файлы к проекту с помощью директивы препроцессора, а все делают это функцией?
          Если я приду в Python и заявлю, что язык ужасный, потому что в нём нет {}, а вместо этого отступы?
          Да, в конце концов, если я приду в любой сильно типизированный язык и скажу, «сильная типизация — это фигня, и поэтому язык плохой»?

          Нет, я так не делаю ). Потому что понимаю, что языки разные, разным людям по-разному удобно, и вообще (как мне кажется) тут имеет место аналог гипотезы лингвистической относительности.
          И мне не совсем понятно, почему вы приходите в JS и заявляете о неправильности this (к слову, я могу назвать ряд действительно слабых (по моему мнению) вещей в js — например, typeof… но уж никак не полностью логичный this).


          1. staticlab
            06.08.2017 23:43
            +4

            При этом около многострочных стоит оговорка: в них нужен return.

            Я бы сказал, что скорее сделано исключение для случая выражения: не нужны ни скобки, ни return.


            1. Keyten
              06.08.2017 23:55

              Да, так вернее, спасибо.


          1. TimTowdy
            07.08.2017 00:21
            +3

            Проблема с this не в том, что он сложен, а в том, что он нелогичен.

            counter.increment() // сделает то что задумано
            call_later(counter.increment) // не сделает вообще ничего и даже не выдаст warning/exception
            


            Попробуйте вспомнить, как часто вы реально использовали великолепную фичу «потеря контекста в колбеке» и как часто вы с ней боролись, прибивая гвоздями контекст чтоб не отваливался (либо костыляли c замыканиями var self = this).

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


            1. ivan386
              07.08.2017 00:37

              Можно так:


              call_later(()=>counter.increment()) 

              Но я даже не представляю в каком языке ваша конструкция отработает верно.


              1. DistortNeo
                07.08.2017 00:44
                +4

                Но я даже не представляю в каком языке ваша конструкция отработает верно.

                Например, в C#.


              1. TimTowdy
                07.08.2017 00:58
                +1

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

                А отработает корректно например в Python: у instance методов контекст не отваливается, у static/class методов контекста нет.
                При этом, если очень хочется, оторвать контекст от метода можно, но мне за последние годы это понадобилось лишь один раз, для настолько магической фичи, что я до сих пор сомневаюсь правильно ли я сделал.


              1. ZyXI
                07.08.2017 00:59

                В Python методы объектов связаны с объектами, поэтому call_later(counter.increment) вполне будет работать. До несвязанного можно достать через counter.__class__.increment (правда, не всегда), вот этому придётся передавать counter в качестве self в любом случае.


              1. TheShock
                07.08.2017 04:49

                Но я даже не представляю в каком языке ваша конструкция отработает верно.

                В очень многих. Например в C#, но это другие языки.


              1. mayorovp
                07.08.2017 07:51
                +1

                Но я даже не представляю в каком языке ваша конструкция отработает верно.

                В Python. Там функция является одновременно дескриптором свойства, что переопределяет поведение при чтении свойства. Для функций при обращении вида counter.increment возвращается специальный служебный объект под названием BoundFunction.


                Ну и про C# уже тут писали. Там в рантайме делегат хранит не только указатель на функцию, но и, опционально, нулевой аргумент. И синтаксис языка позволяет в случаях вроде counter.increment эту возможность использовать.


                1. grossws
                  07.08.2017 08:05
                  +1

                  И в java method reference вида counter::increment отработает так же в контексте объекта.


                  1. staticlab
                    07.08.2017 08:10

                    Это не совсем честно, поскольку всё же требуется двойное двоеточие. Опять же, в stage-0 есть bind operator, благодаря которому на JS можно записать call_later(::counter.increment).


                    1. grossws
                      07.08.2017 08:14
                      +2

                      В java method reference пишется только через ::, других вариантов нет. Не знаю, что уж тут нечестного. В scala IIRC method reference будет выглядеть как counter.increment, если вам так захотелось прицепиться к синтаксису.


              1. senia
                07.08.2017 08:56

                Скорее всего везде, где подобная запись вообще имеет смысл.
                Эта запись чаще является частным случаем частичного применения.
                Рассмотрим общий случай частичного применения (scala):

                arg0.method(arg1, _, arg3, _)
                

                Здесь на основе метода, принимающего 5 параметров (this + 4 явных), определяем лямбду, принимающую 2 параметра.

                И даже если в языке нет полного синтаксиса частичного применения, работает та же логика: если указан параметр, то он применяется. Java:
                Runnable r = counter::increment // без параметр (this применен)
                Consumer с = Сounter::increment // 1 параметр
                


                Проблема здесь в прототипном наследовании. В JS просто нет способа получить метод класса, не указывая конкретный экземпляр (так как нет классов в привычном понимании).
                И в каком-то смысле поведение JS консистентно и логично — функция — это еще 1 поле объекта и, в данном случае, поведение такое же, как и со всеми другими полями. Но this в таком случае имеет смысл совершенно иной, нежели в большинстве других языков.


            1. TheShock
              07.08.2017 04:57
              -1

              Проблема с this не в том, что он сложен, а в том, что он нелогичен.

              Проблема с this не в том, что он нелогичен, а в том, что его логика отличается от других языков.

              как часто вы реально использовали великолепную фичу «потеря контекста в колбеке»

              Вы не понимаете. Это не фича «а давайте терять контекст, вдруг кому-то удобно будет». Это следствие его реализации. this — просто переменная, которая указывает, в каком контексте вызвана функция. То есть контекст не теряется, его никогда и не было, он просто подставляется в момент вызова. И такое следствие вы используете значительно чаще, чем вы думаете (все прототипное наследование на этом построено).

              Вот смотрите.

              var a = { 
                foo: function () { console.log(this) }
              }
              
              a.foo()
              


              Тут вроде логично.

              var a = { 
                foo: function () { console.log(this) }
              }
              
              var b = {
                foo: a.foo
              }
              
              b.foo()
              


              Вот тут могут быть сторонники каждого решения.

              var Proto = { 
                foo: function () { console.log(this) }
              }
              
              var B = function () {}
              
              B.prototype = new Proto();
              
              var b = new B();
              
              b.foo(); 
              


              А вот тут контекст должен указывать на Proto, по вашему, потому что там объявлена функция?

              Другое дело, что в 95% случаев работа с this в JS крайне неудобна и новички об нее постоянно спотыкаются. Это да. Но она по-своему логична.


            1. Keyten
              07.08.2017 13:19
              +1

              Define «логично».

              Я считаю, что this логичен. This хранит контекст, в котором функция вызвана. Ну, TheShock уже всё отлично объяснил.

              Смотрите, вот тут мне нужен родной контекст функции:

              SomeClass.prototype.init = function () {
                this.element.addEventListener('click', this.onElementClick);
              };
              
              SomeClass.prototype.onElementClick = function () {
                this.style.color = 'red';
              };
              // так делать не очень хорошо, но не суть: легко придумывается более адекватный и правильный, но многословный пример
              


              А здесь нужен контекст класса, и я просто делаю bind:
              SomeClass.prototype.init = function () {
                this.element.addEventListener('click', this.onElementClick.bind(this));
              };
              
              SomeClass.prototype.onElementClick = function () {
                this.element.style.color = 'red';
              };
              


            1. fukkit
              07.08.2017 21:43

              Вы передаете в call_later один параметр — адрес метода, но не передаете адрес объекта контекста. Она угадать его должна?


              1. ZyXI
                07.08.2017 21:55
                +2

                Передаётся один параметр — метод. Не адрес метода, метод. Во многих языках (кстати, сейчас к ним присоединился даже дремучий VimL, в котором ни классов, ни прототипов нет, только словари) при таком обращении либо происходит связывание метода с объектом, либо возвращается уже связанный метод (а связывание происходит при создании). Это вполне логично, такое связывание позволяет реализации языка «не знать», как вызывается метод и что следующей операцией вообще будет вызов: просто происходит что?то вроде «оператор точка достаёт атрибут объекта (достаётся уже связанный метод) и кладёт его на вершину стёка, затем оператор скобочки вызывает то, что находится на вершине». Или не вызывает, если такого оператора нет.


                1. fukkit
                  08.08.2017 11:27
                  -1

                  Чистой воды вкусовщина. Лично я не люблю, когда язык навязывает неявные действия. Любите, чтобы в одном параметре передавалось два объекта или некий новый костыльный метаобъект? Не вопрос, есть такие языки.

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


                  1. TimTowdy
                    08.08.2017 14:01

                    Вы о чем? this в JS как раз и является максимально неявным способом передачи дополнительного параметра в функцию. Вас почему-то не смущает неявный костыльный метаобъект, который передается при вызове counter.increment().

                    <expression1> == <expression2> // true
                    <expression1>() // делает мяу-мяу
                    <expression2>() // не делает ничего
                    

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

                    Вы ему: передай той функции метод этого объекта (функцию, которая умеет обрабатывать любой объект данного класса, у нас типа ООП),

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

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

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


                    1. TheShock
                      08.08.2017 14:39
                      -1

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

                      Увы, нету в JS классов в таком виде. Есть функция первого класса, которая создает объект и которому присваивает прототип. И даже нету методов. Есть функции, которые лежат в прототипе.

                      У нас даже нету «это объект класса А». Нет. Скорее так: «у этого объекта сейчас прототип ссылается на инстанс А». А может потом вдруг начать ссылаться на что-то другое

                      Не мыслите о this в JS с точки зрения C#, это не имеет никакого смысла


                      1. TimTowdy
                        08.08.2017 17:59

                        Классов нет, но абстракция «метод объекта» есть, именно поэтому контекст неявно и подставляется в counter.increment(). Проблема в том, что абстракция часто течет, например при работе с колбеками. Тот факт, что проблема объясняется устройством языка, не отменяет само существование проблемы.

                        Но веселье call_later(counter.increment) ведь не только только в контексте. Мало того, что язык легко позволяет допустить такую ошибку, так он еще и без проблем обратится к несуществующему свойству у window, извлечет undefined и увеличит его на 1. Никаких ошибок в процессе.

                        Я мыслю не с точки зрения C# (последний раз писал на нем более 10 лет назад), а с точки зрения того, каким должен быть промышленный язык. Особенно когда у него появляются ярые фанаты, которые считает что на нем можно (и нужно) писать абсолютно все.


                        1. TheShock
                          08.08.2017 18:39
                          -1

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

                          О том, что это крайне неудобно — я совершенно не спорю. В том же C# лично мне этот момент нравится значительно больше — оно более интуитивно-понятно и значительно приятнее используется.


                        1. vintage
                          09.08.2017 14:41
                          +3

                          без проблем обратится к несуществующему свойству у window

                          Это давно уже починили в strict mode.


              1. DistortNeo
                07.08.2017 22:10
                +1

                Она угадать его должна?

                Но ведь угадывает же при вызове counter.increment().


                1. fukkit
                  08.08.2017 11:37
                  +1

                  В контексте вызова counter.increment() существует вычисляемое значение counter, потому угадывать ничего не нужно.

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

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


                  1. DistortNeo
                    08.08.2017 15:35
                    +1

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

                    Я считаю, что должен. А если я не хочу, чтобы контекст тащился вместе с методом, я просто создам статическую функцию increment с параметром counter и будут передавать "контекст" в виде counter в явном виде.


                1. yhaskell
                  08.08.2017 11:43

                  На самом деле она не указывает. Запись a.foo() обозначает "вызвать метод a.foo в контексте с a". Просто контекст по умолчанию не bound, как в Python.


  1. Bonart
    06.08.2017 03:26
    +4

    Однако у автора чуткий инстинкт самосохранения. Ни единого слова про язык Go.


    1. pnovikov Автор
      06.08.2017 06:45
      +8

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


      1. alexeyknyshev
        06.08.2017 09:30

        Иногда eventual-consistency вполне достаточно, при необходимости можно сделать 2-phase commit.


        1. pnovikov Автор
          06.08.2017 09:39
          +5

          Тут проблема в другом. Любители go обычно топят за микросервисы со страшной силой. Так вот меня интересует — если у вас один микросервис А обращается к микросервису Б, а тот в свою очередь — к миросервису С, то
          а) как передать контекст транзакции между ними?
          б) как откатить результаты работы микросервиса А, если микросервис С по какой-то причине выдал ошибку?


          Здесь нужна некая третья сторона, которая, как ни странно, координирует распределенную транзакцию. Логично? Ну и вот ответа на этот вопрос мне пока ни один адепт go не дал. И я уже молчу о сложных кроссистемных случаях вроде транзакционная запись в файл + в базу (если ошибка — откатывается и то и другое).


          В остальном — гугл неплохо пишет на go всякие низкоуровневые инструменты вроде небольших галактик серверов баз данных. И это определенно хорошо, но ИМХО не дает повода писать на go бизнес-логику и бекенд.


          1. farwayer
            06.08.2017 10:19
            +2

            Осмелюсь порекомендовать

            вот этот доклад


          1. fcoder
            06.08.2017 20:57
            -1

            В контексте микросервисов это слишком общий вопрос. Тут нужно уточнять конкретный кейс.

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

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


          1. uvelichitel
            07.08.2017 00:17

            Микросервис он как настоящий, только маленький. На Go я могу поднять такое на самом дешёвом VPS/VDS, на домашнем rapsberry за день работы или на C за месяц или на C# на физически выделенной железяке.


            1. SirEdvin
              07.08.2017 18:44
              +1

              Использую python, могу поднять свой микросервис на домашнем rapsberry. Да, он занимает немного больше памяти, но только лишь немного)


          1. pawlo16
            07.08.2017 09:14
            -9

            координатором распределенных транзакций

            так этого не только в Гоу нет — python, erlang например. Есть только в java и С#, что легко объяснить — в кондовых ООП-языках количество бойлерплейта для реализации такого рода машин состояния настолько огромно, что ни один мидл-программист его нормально не осилит. В go (как и в python) есть несколько популярных фреймворков для конфигурации и управления микросервисами, которые делают вашу задачу с распределёнными транзакциями не многим сложнее задачи управления локальными переменными внутри блока.


            транзакционная запись в файл + в базу (если ошибка — откатывается и то и другое).

            запасся попкорном, с удовольствием послушаю какие тут есть проблемы в Гоу и каким образом C# конкретно в данном аспекте поможет Гоу программисту


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

            Очевидно ваше утверждение противоречит практике. Существуют тысячи success stories распределенных отказоустойчивых, сложных систем на Go, в том числе в сегменте среднего бизнеса. Например телекоммуникационная платформы для операторов связи от ITooLabs (http://itoolabs.com). Есть тысячи опердней со сложной бизнес логикой на Гоу


            1. pnovikov Автор
              07.08.2017 09:15
              +7

              Господи, мы все уже поняли что вы — горе-евангелист go. Хотя бы этот тред не засоряйте. Ну пожалуйста.


              1. pawlo16
                07.08.2017 10:15
                -9

                Ok. Предпочитаете кривляться и валять дурачка вместо того чтобы ответить за свои слова — слив засчитан, разговор окончен. А давайте вы тоже в качестве ответной любезности не будете больше писать откровенные глупости про "не дает повода писать на go бизнес-логику и бекенд", на которые не способны дать ни малейшего разумного обоснования, а?


                PS: стесняюсь спросить, "мы все" — это кто, и почему вы считаете себя "их" фронтменом?


                1. pnovikov Автор
                  07.08.2017 10:17
                  +7

                  Вас вообще не смущает сколько людей минусуют ваши комментарии? Может уже пора задуматься?


                  1. pawlo16
                    07.08.2017 11:24
                    -7

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


                    1. pnovikov Автор
                      07.08.2017 11:27
                      +5

                      "Я — красавец, go — няшка, а вы все — быдло", я правильно вас понял?
                      Вас мама в детстве не учила что так вести себя некрасиво?


                    1. 0xd34df00d
                      07.08.2017 19:43
                      +1

                      На мой ответ про плюсы с хаскелем не ответили вы в соседнем треде (или в этом, я уже запутался, multithreading is hard), а мартышка, которая ничего сказать не может — всё равно я.


            1. SirEdvin
              07.08.2017 18:48
              +1

              Очевидно ваше утверждение противоречит практике. Существуют тысячи success stories распределенных отказоустойчивых, сложных систем на Go, в том числе в сегменте среднего бизнеса. Например телекоммуникационная платформы для операторов связи от ITooLabs (http://itoolabs.com). Есть тысячи опердней со сложной бизнес логикой на Гоу

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


      1. farwayer
        06.08.2017 09:51
        +3

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

        вот такую штуку :)
        image


  1. SDSWanderer
    06.08.2017 04:11
    +1

    Статья в двух словах:


    Ей, вы там, js разработчики, да, я не спец в этом вашем js, но слушайте меня, я вам сейчас поставлю диагноз!

    И не лень же людям столько текста писать!


    1. mogaika
      06.08.2017 11:06
      +2

      Комментарий в двух словах:

      Ей, вы там, читатели комментариев, да, я не читал статью, либо не понял смысла, но здесь, похоже, затронуты мои чувства!
      И не лень людям комментарии оставлять


  1. Tantrido
    06.08.2017 04:12

    Прекрасная статья, посмеялся, особенно видео про JS WAT :-)


  1. ankh1989
    06.08.2017 04:40
    +3

    После 9-го уровня возникает какое то безразличие ко всем этим джаваскриптам и на такие вопросы флегматично отвечаешь, что не видишь большой разницы между Java и JavaScript. К слову, на одном собеседовании в Амазон я также флегмантично ответил — интересно было реакцию посмотреть, а мне в ответ "значит вам всё равно на каком языке писать? прекрасно!"


    1. pnovikov Автор
      06.08.2017 06:46
      -1

      Это отходняк и деменция, друг мой. Последствия быстрого слезания с JS.


  1. lxsmkv
    06.08.2017 06:25
    +3

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


    1. pnovikov Автор
      06.08.2017 06:57
      -2

      Есть в этом доля правды. Но я же не могу написать что все программисты JS — плохие программисты. Во-первых в общем случае это не так, хотя среди них и много "нанятых по объявлению". Во-вторых тапками закидают. В-третьих это просто некрасиво.


      А вот есть ли среди человеческих языков те, которые всерьез разъедают мозг переводчика — я бы с радостью послушал. :)


      1. lxsmkv
        06.08.2017 14:34
        +4

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

        Наверно правильно не язык ругать, а то что его трудно учить. Наверно это будет более объективной критикой.


      1. Keyten
        06.08.2017 16:50

        Есть аналоги Malbolge — ифкуиль, например


      1. AnutaU
        06.08.2017 21:07

        Вышеупомянутый корейский — ещё какой мозговирус. У меня, кажется, уже терминальная стадия КГМ: я захожу на Хабр почитать про языки программирования, а комментарий пишу, кхм, всё про то же.


    1. slonopotamus
      06.08.2017 13:09

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


      1. lxsmkv
        06.08.2017 14:44
        +2

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


    1. uvelichitel
      06.08.2017 16:57

      Испанский и корейский это естественные языки. А вот эсперанто искусственный, его можно ругать.


  1. Mycolaos
    06.08.2017 06:43
    +2

    … со временем превращает пишущего на нем человека в агрессивного фанатика, чего мне не удалось отметить с другими технологиями.

    Лукавите.


  1. borisxm
    06.08.2017 07:45

    В каждой шутке, есть доля шутки. У нас в компании C++/Java/Pascal разработчику никогда не поручат написать на JS что-то сложнее приветмира. А JS-программисты должны держать себя в рамках, чтобы не достигнуть четвертого уровня.


  1. justboris
    06.08.2017 08:50
    +1

    Здесь ты начал использовать npm

    Почему вы считаете использование npm чем-то "больным"?


    По вашему мнению, нормальные люди не пользуются менеджерами пакетов?


    1. pnovikov Автор
      06.08.2017 08:57

      Это просто хронологическая метка, не более


  1. ju5tify
    06.08.2017 09:14
    -18

    Единственной что я выношу из подобной статьи — у автора подогрело от того, что в 2017 году за JS платят как минимум в полтора раза больше чем за такие любимые и строжайше типизированные и трижды многопоточные плюсы или ещё там что-то.
    Как же это так? Сраный джаваскрипт, на котором ещё вчера делали выпадающие менюшки, теперь является одним из самых популярных и высокооплачиваемых инструментов разработки! А как же Страуструп? Как же моя магистратура? Что же это делается такое, а? Беспредел! Пойду напишу разоблачающую статью о том какое JS говно. Ее все прочтут и сразу прекратят писать на нем и снова мой "правильный" язык будет править миром (и зарплатами на Stackoverflow careers).
    М — многоходовочка.


    1. pnovikov Автор
      06.08.2017 09:19
      +11

      Мне очень льстит что вы так беспокоитесь за мое финансовое положение, но все же уверяю вас — с ним все в порядке. Настолько в порядке, что мне иногда даже стыдно становится :)


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


      1. ju5tify
        06.08.2017 10:42
        -11

        Ого, круто то как! Вот это я понимаю успешная карьера! И зарплата настолько большая, что аж стыдно называть, и хвастаться не хочется (на самом деле хочется :)), и работа в столице.
        Все настолько хорошо, что аж появилось желание и уйма времени написать огромный трактат про недостатки совершенно чужого языка. Действительно, зачем? Вот я тоже получаю миллиарды заграницей (мы все тут богатые и успешные, да), но мне и в голову не приходило написать пьесу по полям о недостатках Rust. А все потому что он мне нахер не нужен. А что ж вас тогда этот убогий JS беспокоит?


        1. pnovikov Автор
          06.08.2017 10:47
          +9

          Я вам секрет открою — у меня еще время есть написать парочку opensource-фреймворков, один из которых на 60% состоит из кода на TypeScript и, как вы понимаете, это означает что JavaScript меня беспокоит, в отличие от Rust и Go (см. шапку статьи). И работаю я далеко не в столице, а прямо из дома.


          Зачем писать статью? Ну например чтобы посмотреть как у вас припекает :)


          1. staticlab
            06.08.2017 11:54

            Открыл Reinforced.Lattice/CoreInterfaces.ts, а там 26 any. Туда действительно можно засунуть что угодно, или просто описать типизированно слишком муторно?


            1. pnovikov Автор
              06.08.2017 11:56

              Действительно можно засунуть что угодно. В каждый any ставится пользовательский тип данных, который пользователь может сам задать из C#-части. Как вы понимаете, этой информацией невозможно обладать на момент написания Lattice


              Бтв, доку с примерчиками тоже посмотрите чтобы понимать что оно делает. Там не дописано 80%, но даст примерное понимание что я делаю.


              1. PsyHaSTe
                07.08.2017 19:35
                +1

                Я правильно понимаю, что From может быть string, а to — DateTime? Если нет, то почему бы не сделать было IPrecomputedRange<T>, чтобы пользователь сам явно указывал необходимый тип? Потому как мне кажется, что IPrecomputedRange<string> != IPrecomputedRange<DateTime>, а у вас по сути они одного типа..



                1. pnovikov Автор
                  07.08.2017 21:11
                  -1

                  Вспомнил.
                  Слушайте, вы точно контекстно понимаете как используется эта структура, чтобы комментировать? Просто если я тут буду объяснять — будет пара экранов. Но в двух словах — да, any там — это ок, пользователь эту структуру не видит. Она сделана export из-за того, что в typescript нет internal.


                  1. PsyHaSTe
                    07.08.2017 21:43
                    +1

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


                    module Reinforced.Lattice.Filters.Range {
                        export interface IRangeFilterUiConfig
                        {
                            ColumnName: string;
                            FromPlaceholder: string;
                            ToPlaceholder: string;
                            InputDelay: number;
                            FromValue: string;
                            ToValue: string;
                            ClientFiltering: boolean;
                            ClientFilteringFunction: (object: any, entry:Reinforced.Lattice.IPrecomputedRange, query: IQuery)=>boolean;
                            CompareOnlyDates: boolean;
                            Hidden: boolean;
                            DefaultTemplateId: string;
                            InclusiveLeft: boolean;
                            InclusiveRight: boolean;
                        }
                    }

                    Нельзя сразу показать, что IPrecomputedRange тут от типа string? У нас есть FromValue — string, но ничто не мешает запихнуть любой object в качестве FromValue, причем ToValue может быть вообще третьим типом, несовместимый с этими двумя.


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


                    1. pnovikov Автор
                      07.08.2017 21:57

                      Нет, нельзя. Потому что конкретный тип range-фильтра задается пользователем на сервере и выводится из C#-класса. Получается что клиентский typescript-интерфейс (которого не существует после сборки) должен параметризовываться серверным C#-типом. В связи с чем я повторяю свой вопрос — вы точно понимаете как работает этот код?


                    1. pnovikov Автор
                      07.08.2017 22:04

                      Бтв, здесь FromValue/ToValue — сериализованное значение для начальной конфигурации range-фильтра. Циферки просто парсятся, дата — десериализуется из ISO, строки остаются как есть. Более того этот класс (Config) — автоматически сгенерирован из C#-кода. Вам точно хочется в этом разобраться или вы с какой целью интересуетесь?


                      К слову: в C# коде я тоже часто использую object, что не означает что я должен незамедлительно перевести этот код на generic-и.


                      1. PsyHaSTe
                        08.08.2017 13:49

                        Бтв, здесь FromValue/ToValue — сериализованное значение для начальной конфигурации range-фильтра. Циферки просто парсятся, дата — десериализуется из ISO, строки остаются как есть. Более того этот класс (Config) — автоматически сгенерирован из C#-кода. Вам точно хочется в этом разобраться или вы с какой целью интересуетесь?

                        Да, я пытаюсь разобраться, о чем я сразу и сказал.


                        К слову: в C# коде я тоже часто использую object, что не означает что я должен незамедлительно перевести этот код на generic-и.

                        Зависит от использования. Если это условный params — то не вопрос, а если условный object sender — то увольте.


                        1. pnovikov Автор
                          08.08.2017 13:49

                          Пишите в личку тогда — я вам отдельно расскажу. Там реально все сложно и информации очень много.


          1. ju5tify
            06.08.2017 12:45
            -13

            • Маам, смотри, я траллю!
              Серьезно? Успешный высокооплачиваемый разработчик тратит своё время чтоб потроллить бичей вроде меня?
              И не забудь ещё пару ссылочек кинуть, а то вдруг кто-то пропустит этот пиар и не поставит звёздочку на гитхабе :) :) :)


            1. Hardcoin
              06.08.2017 18:25
              +1

              А в чем проблема? Успешный разработчик должен тратить всё своё время, что бы зарабатывать бабки и становиться ещё более успешным? :)


              1. 0xd34df00d
                06.08.2017 19:46
                +2

                Чтобы ездить по конференциям и общаться с единомышленниками.


    1. 0xd34df00d
      06.08.2017 09:43
      +8

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


  1. herr_kaizer
    06.08.2017 09:36
    +8

    В статье упущены всякие PhoneGap, Electron и прочие. Вот уже где апофеоз бесполезности и фанатизма.

    Скрытый текст
    image


    1. pnovikov Автор
      06.08.2017 09:44
      +2

      Ну кстати концептуально насчет бесполезности Electron я бы не согласился. Но только концептуально, то есть "ну наверное было бы неплохо писать десктопные приложения на html+js" (тем паче что многие так делают). Но в деталях реализации я ни черта не смыслю, поэтому не упоминаю.


    1. Sad_Bro
      06.08.2017 10:20
      +2

      Своя ниша есть, почему нет.
      Пишутся же не плохие приложения на electron, тот же Sluck, Postman да еще и под все OS.


    1. Alex_ME
      07.08.2017 00:18

      Сам отношусь к js-вакханалии примерно как описано в статье (основной язык — cs). Но тут надо делать одно небольшое веб-нечто, да еще требуют мобильное приложение. Я подумал, и решил использовать React + Cordova, чтобы сэкономить время, силы и прочее.


      Доктор, на какой я стадии?


      1. pnovikov Автор
        07.08.2017 00:21

        Ну… вы по крайней мере ещё умеете думать сами. Так что лично я вас благословляю.


  1. Antelle
    06.08.2017 11:39
    +2

    Нет уровня "да наплевать на чём писать, главное чтобы это решало проблему, если это для вас на сейчас javascript, то и юзайте".


  1. Large
    06.08.2017 12:33
    +2

    Плохому танцору…


  1. TimTowdy
    06.08.2017 13:10
    +13

    Жду ответной статьи «Джаваскрипт как религия». С описанием почему именно эта религия единственно правильная и всем нужен радикальный Джаваскрипт. С пошаговым руководством как взрывать проекты написанные на технологиях неверных, чтобы потом переписать их на единственно верном технологическом стеке. Каждый кто перепишет проект на JS — получит в награду 72 сервера с NodeJS (после банкротства).

    А если серьезно, такие фанаты появляются в основном у хайповых языков, с явными общеизвестными недостатками. Ну там типизация в Джаваскрипт или дженерики в Go. У хипстеров возникает чувство неполноценности, и они таким образом борются с ней. «От слабой типизации мы только выиграли». «ДЖЕНЕРИКИ НЕ НУЖНЫ!»

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


    1. farwayer
      06.08.2017 13:36
      +4

      Да фигня в том, что часто происходит как раз наоборот. Приходит такой чувак из Java (спокойно, это только пример) к go-разрабам и говорит: go — говно, потому что в нем нет дженериков. А те сидят, хлопают глазами и думают, как объяснить чуваку, что у них и так все хорошо.


      1. TimTowdy
        06.08.2017 14:29
        +5

        Вот об этом фанатизме я и говорю, отрицание вполне объективных проблем потому что «у нас все и так хорошо».

        Роб Пайк вообще-то признает, что не все хорошо: «A weakness of Go is that any generic-type operations must be provided by the run-time. Some day that may change»

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


        1. pnovikov Автор
          06.08.2017 14:39
          +2

          Напомнило замечательную историю про Java, C# и лямбда-выражения. Когда их сделали в C#, джависты хором кричали — "нинужно!". А когда ввели и в Java — "как же мы раньше без этого жили-то божечки?"


    1. S_A
      06.08.2017 15:35
      +5

      Вы знаете, я писал за деньги на C/C++, C#/F#, Java, Swift, Python, PHP, Javascript/Coffeescript/Typescript. На гироскутере в барбершопы не езжу. И считаю стэк JS/node.js наилучшим что есть в настоящее время.

      Знаете почему? Потому что в его экосистеме из п. А в п. В я быстрее всего доберусь. Единственное, что я не доверяю пока JS — это мобильные приложения (React Native очень сырой, ненативные гуи не люблю). Предпочитаю Xamarin (и нет повода для жалоб). Nuget — следующий за NPM репозиторий, позволяющий не писать, а конструировать приложения (получая за это деньги, а не ЧСВ).

      Вобщем не язык решает. Хайпа хватает и в тусовке шарперов, и джавистов. Плюсисты вообще смотрят на всех как на детей :) А кто на ассемблере пишет, тот «в цирке не смеется».


      1. TimTowdy
        06.08.2017 16:43
        +1

        Я в одном из прошлых тредов уже писал, что жизненный цикл проекта обычно несколько сложнее чем добраться из п. А в п. В.
        Подход «максимально быстро из п. А в п. В» например приводит к максимально быстрому накоплению технического долга. Иногда стоит задуматься о том, что пункт В — не конечная остановка. У бизнеса конечеой остановки может вообще не быть.
        Если вам нужно срубить 5 деревьев, можно взять тупой топор. А если деревья нужно рубить каждый день, возможно стоит инвестировать в бензопилу.

        Хотя конечно бывают сферы где для разработчика это зачастую неважно (тот же фриланс).

        Ну а с тем, что наколеночные прототипы на JS делаются быстрее, чем на каком-нибудь Java, вроде никто и не спорит.


        1. S_A
          06.08.2017 17:37

          Тот же Angluar веьма SOLID. И те же админки на том же Zend >= 2 я забрасывал не одну (и не с одним проектом на них уже) за то что они долгие.

          p.s. во фрилансе никогда не работал.


          1. TimTowdy
            06.08.2017 18:26
            +1

            Тот же Angular почему-то решил перейти на TypeScript.

            Но проблема JS не в отсутствии фреймворков или библиотек (как раз наоборот, google: drinking game for web developers).

            Проблема в том, что он не задумывался как промышленный язык, но в какой-то момент стал им. И в отличие от других пром. языков он не может развиваться, ломая обратную совместимость. Его посыпают сахаром и подпирают костылями, но 20-летние детские проблемы от этого не уходят. Это — объективно плохо. С этим можно жить, но этому глупо радоваться.


            1. S_A
              06.08.2017 18:44

              Я не вижу проблем с Typescript --> Javascript. Есть fullstack фреймворки — fireloop например — которые прекрасно работают только с Typescript.

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


  1. Nakosika
    06.08.2017 13:34
    +17

    Вся драма ява скрипта в том, что ВЫ ЕГО НЕ ВЫБИРАЛИ.


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


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


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


    И это беда всей нашей индустрии — обычно мы не выбираем лучшие технологии просто потому что они лучшие для нас. Другие делают этот выбор.


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


    Но удел большинства все-таки идти за трендом. Просто потому что это тренд, который никто не выбирал.


  1. Andchir
    06.08.2017 15:00
    +1

    Как же достали статьи из разряда «я пишу на правильном языке, а вы все мудаки». Остановите этот поток бесполезного самолюбования на Хабре.


  1. ua_lost_in_the_dark
    06.08.2017 15:09
    +5

    Отложи JavaScript, освой какой-нибудь игровой движок. Тебе срочно необходимо отогнать от себя мысли о JavaScript

    И тут такой скачиваешь Unity 3D и начинаеш писать на UnityScript.


  1. khorn_sv
    06.08.2017 15:09
    +7

    Странная, ей Богу, статья. Да и профиль у вас чертовски странный:

    делать всё, чтобы защитить бедных C# разработчиков от излишнего ныряния в JavaScript
    Особенно в том, что касается «фанатизма» странности. В самой статье, да и в комментариях под ней вы упорно пишете, что вас раздражает не сама технология, но скорее ее через-чур ярые поклонники и высокомерие, с которым они смотрят на «не причастившихся».
    Но, вот ведь парадокс, сама статья является воплощением подобного же высокомерия. Так как она буквально вопиет о важных умолчаниях: «те, кто выбирают .js добровольно — с вирусом в голове», ну и то, что ваш стек технологий и знаний по умолчанию дает вам моральное право смотреть на этих бедолаг с высока и рассуждать о том, что хорошо, а что плохо.
    Но за юмором, за фразочками «я не имею ввиду технологию, а только фанатиков от нее» не скрыть негативных коннотаций. Все то же необоснованное высокомерие на пустом месте.
    Не понимаю, честно, цели написания статьи. И так ведь понятно, что все «фанатики» — это в большинстве своем неофиты. Но ведь не только они выбирают этот язык в качестве инструмента. Есть и матерые зубры, которые с энтузиазмом относятся к нему.
    Но умолчания… Серьезно, сначала вы называете какую-то особенность языка и потом говорите, что если человеку она нравится, то он «болен». Но ведь нет, вы же не имеете в виду всех, а лишь только «фанатиков». Мастерство риторики — запредельное. Сначала обгадить людей, хочу подчеркнуть, исключительно на основе того, что отношения к одному и тому же языку разные, а потом сделать вид, что этого как бы и не было.
    И главное, чего добиться то хотели не понятно. Остроумием блеснуть за счет того, что потешаетесь над другими.
    Очередной высокомерный повод для холивара на пустом месте.


    1. pnovikov Автор
      06.08.2017 15:26
      +4

      Вы нашли в моем тексте какой-то странный, неведомый мне доселе подтекст. Если бы я действительно хотел полить JS фекалиями, а всех, кто на нем пишет назвать идиотами, то я сделал бы это в куда более жесткой форме, без юмора и аргументировано, с техническими выкладками. Но я признаю право тех, кто пишет на JS писать на чем им вздумается и как им вздумается, равно как и право любого человека любить все, что душе угодно — хоть тот самый '3' + 2.


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


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


      все «фанатики» — это в большинстве своем неофиты

      Доводы про строгую типизацию и многопоточность я услышал от senior js developer-а, который занимается программированием то ли 10 то ли 15 лет. Dixi.


      Мастерство риторики — запредельное

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


      1. Andchir
        06.08.2017 16:02
        +1

        Обратите внимание на 8 пункт.

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


        1. pnovikov Автор
          06.08.2017 16:05

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


      1. uvelichitel
        07.08.2017 00:49

        Писать на JS не право а долг сегодняшнему web. Кто то должен это делать, а то упадут небеса. Выбора ведь нет. А без игрух на unity ещё пока можно обойтись. Пока C# экзистенциальный выбор, а не священный долг.


    1. fukkit
      06.08.2017 18:53

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

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


  1. zKey
    06.08.2017 17:58

    Интересная статья. Видно проблема назревает повсеместно, не только у нас в компании. С отделением фроненда и выносом его на ноду проблем особых не было. "Так модно, так современно". Появился независимый деплой фронта и бека. Теперь фронт может релизиться в два раза чаще. Нам понравилось) но JS-совцы решили пойти дальше и начали затягивать бизнес логику на фронт. Почему-то начали переписывать полностью готовые сервисы, которые никому не мешают на JS. У нас начался треш и угар. У кого-нибудь есть такое?


  1. Fen1kz
    06.08.2017 18:22
    +4

    Я определил у себя 7 стадию, уже собирался действительно пойти работать грузчиком, но на полпути дошло, что пред-терминальную стадию у меня диагностировал сишарпист. No way, вы же проклятые микрософт-зомби =/


  1. sbnur
    06.08.2017 18:24
    -4

    Мысленно убираем браузеры — и где видится javascript?


    1. Keyten
      07.08.2017 17:16

      1. sbnur
        07.08.2017 17:24

        Вы меня убили (фигурально конечно)
        Никогда не говори никогда (это я себе)
        PS — а мне казалось (по собственному опыту), что для микроконтроллеров достаночно c или lua


        1. TheShock
          07.08.2017 18:19

          А какая разница — JS или Lua?


          1. grossws
            07.08.2017 18:25

            Lua куда проще и прямолинейней. Простая стековая машинка, легко встраивается. Но зачем оно на контроллере со, скажем, 4k RAM мне не очень понятно.


            1. Xitsa
              15.08.2017 09:34

              У lua — регистровая машина.


              Lua is dynamically typed, runs by interpreting bytecode with a register-based virtual machine


          1. sbnur
            07.08.2017 18:32

            в обозримости


      1. grossws
        07.08.2017 18:24

        Для игрушек — да. Для чего-то серьёзного отдавать 350k медленному интерпретатору, лишаться всяких прелестей типа DMA и т. п. Подобные вещи оставляют этот проект в нише ардуино, но на более мощном контроллере (хотя сейчас уже есть ардуины на cortex-m).


        Вон в соседней теме автор радостно заявляет:


        Вводит в заблуждение приставка Script и несерьёзный имидж языка, а на деле обнаруживается, что язык применяется от front-end и back-end до дескопных и мобильных приложений, программирования интегральных микросхем, обработки видео и в множестве других сфер.

        Но пока видится только фронт, некоторый кусок бэка, некоторое количество десктопных приложений на электроне, react native и хватит.


  1. devalone
    06.08.2017 18:35

    А ещё есть уникумы, которые считают, что писать на js десктопные приложения и прошивки для микроконтроллеров — норма.


  1. ZhetoN
    06.08.2017 19:11
    +4

    Пишу на JavasSript /* современном :) */, PHP, Python, C, C# – обожаю их всех. Хотя, в подобных статьях, в комментариях всегда интересно.


  1. pawlo16
    06.08.2017 20:20
    +1

    Немедленно достань учебник по C++

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


  1. teux
    06.08.2017 22:38
    +1

    Голосую за писательский талант автора )

    C JS пятый год. Бывает позыв бросить, перейти на какой-нибудь C(#++). Но JS тут ни при чем.
    Скорее тяготит безмерности спектра применений JS.

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

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

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

    Вот и думаешь иногда, метнуться на какой-нибудь C(#++), может даже вспомнить Assembler, на котором писал когда-то драйверы. Сидеть себе копаться в однотипных задачах. Развлекать мозг алгоритмами, а не поиском npm-пакетов.

    Но потом понимаешь, будет скучно без ощущения вечного ренессанса, без камасутры react/redux/apollo/immutable/draft/jest/eslint/node/npm/sass/webpack/babel/uglify/pm2. И не можешь никуда деться. Все симтомы болезни налицо )


  1. archik
    07.08.2017 00:03
    +1

    Странно, что страйки многопоточности до сих пор летят в сторону JavaScript. В чисто виде, конечно же, таких механизмов в языке нет, но в браузере вы всегда сможете найти Web Workers, а в Node.js многопроцессорность с IPC и child_process. И это все, если очень хочется. Однако не надо забывать, что каждый «молоток» для своих «гвоздей».

    «Компьютер — это конечный автомат. Потоковое программирование нужно тем, кто не умеет программировать конечные автоматы»

    Алана Кокс, один из ведущих разработчиков ядра Linux



    1. uvelichitel
      07.08.2017 01:35
      +2

      Видимо неточный перевод, программа это конечный автомат. Компьютер это машина Тьюринга способная воспроизвести любой конечный автомат, исполнить любую программу. О взаимодействии двух машин Тьюринга с пересекающимися алфавитом можно почитать в CommunicatingSequentialProcesses Tony Hoare. Такой вычислитель всегда теоретически возможно построить, но не всегда практически.


  1. staticlab
    07.08.2017 00:32
    +2

    Кстати, доктор, а какая стадия у авторов библиотек, позволяющих писать такое? https://github.com/artptr/typed-server-example/blob/master/src/controller/ArticleController.ts


    1. pnovikov Автор
      07.08.2017 00:37
      +2

      Это мета-стадия, находящаяся за пределами классификации. Люди явно хотят ASP.NET MVC, но почему-то повторяют его на TS


  1. uvelichitel
    07.08.2017 01:58

    Автор, почему при таком неприятии overengineering JavaScript вы беретесь пилить свой 100500s vdom. Задача алгоритмически давно решена-diff() и patch() на дереве DOM, не представляет вызова для квалифицированного разработчика, прекрасно и неоднократно решена практически открытым кодом. Ценность подхода спорна. Почему не оставить предмет тем, для кого это хлеб насущный? Вы не инфицированы? Почему не порешать, недорешенную задачу распределенного консенсуса актуальную для микросервисов или block chain?


    1. pnovikov Автор
      07.08.2017 04:52
      +2

      Ну во-первых это все же немного не ваше дело, чем мне заниматься и какие задачи решать. Вы меня, при всем уважении, на работу не нанимали и задачи не ставили. Во-вторых, это ни для кого кроме меня не есть хлеб насущный, ибо как шаблонизатор тесно интегрирован с C#, разбираться в котором для "настоящего шотландца JS-разработчика" недостойно внимания. В-третьих — вот дорешаю задачу с vdom-ом у себя, потом займусь распределенным консенсусом. Если захочу.


      В-четвертых, я кажется сказал, что у меня нет претензий к JavaScript как к технологии. Нет? Я тихо сказал? Или у вас каждое 5 слово в тексте произвольно заменяется на "overengineering"?


      1. uvelichitel
        07.08.2017 23:08
        +1

        Просто я тоже написал vdom. Это оказалось не сложно, но в конце я не понял зачем. Консенсус, византийские генералы это ни разу не просто, даже алгоритмы не все на Wikipedia лежат и у Кнута мало.


  1. iit
    07.08.2017 07:27
    +6

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


    Видел я конторы которые пишут на c# методом копипаста не зная технологии как таковой (сам я тогда писал на visual basic в этой конторке).


    Видел как мой тесть который всю жизнь делает системы на asm/c++ для кристаллов на сайте тещи который на php определил hex константы и определял mime загружаемых файлов читая в потоке несколько байтов файла и прочитанное побитово сравнивал с hex и определял mime по итоговой маске, хотя это решается встроенной функцией на раз-два.


    В нашей конторе есть пара фронтов, что поддерживают проект на backbone.js и перепиливают его на vue.js, есть отдел из java маньяков которые пишут суровый javaEE копипасятя код из stack overflow и деплоят его на glassfish.


    Есть и несколько php вроде меня которые пишут api пол 100500 сервисов и партнеров. Но вот бизнес и тим лид почему-то решили что внезапно лендос должен быть сделан на java+jsp+backbone/vue а вот CRM сделанную изначально на delphi и падающую 10 рад на дню нужно переписать внезапно на php.


    И вот я блуждая по клубку процедур и функций в main.pas (благо паскаль в школе учил) вытаскиваю доменную логику и сую её в shedule комманды на laravel одним глазом наблюдая как чуваки с команды vue.js ругаются с java ситхами кто какую бизнес логику будет делать и на каком уровне за глаза называя друг друга нелестными словами.


    В итоге я сделал вывод что все безумны и я в псих больнице я тупо дальше пилю скоринг на php, crm на php и еще кучу вещей на php которые не должны быть на php. В то время как эту чуваки уже 13 месяцев не могут ни допилить старый леднинг на backbone+javaEE ни переписать его на vue+javaEE.


    Итог:


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


  1. djw
    07.08.2017 08:33

    Ну вот почему так-то? Ведь все же знают и про KISS, и про каждой задаче свой инструмент, но всегда начинается дикая полемика (а иногда и с переходом на личности, что страшно), стоит кому-то просто высказать свое мнение…
    Естественно, каждому ленивому (в хорошем смысле слова) программисту хочется иметь универсальный инструмент на все случаи, но такого не бывает. Дайте людям найти свои грабли и набить свои шишки.
    Ну хотят люди писать бэкенд на JS, так пусть пишут (хотя я противник этого действа, см. каждой задаче свой инструмент), а вдруг, что толковое получится, ведь выбился же JS из роли "часики на страничке" на роль полноценного и самодостаточного языка для фроненда.
    Кстати, и статью и комменты было интересно почитать.


  1. Kroid
    07.08.2017 12:01
    +4

    Пора создавать пост «JavaScript как JavaScript».


  1. Tim152
    07.08.2017 16:46
    -1

    а где шутки про вейпы, смузи и гироскутеры?


    1. timiskhakov
      10.08.2017 15:19
      +3

      Они наконец-то всем надоели.


  1. saintbyte
    08.08.2017 11:28

    На днях смотрел исходники Radium-Keylogger думал что код так себе и надо писать такой же но круче на js. Это у меня какой уровень?


  1. Juribiyan
    08.08.2017 13:47
    +4

    Как пациент, обнаруживающий у себя многие из описанных симптомов, могу предположить следующее: фанатизм джаваскриптеров может быть своеобразным защитным механизмом. Годами нас считали людьми второго сорта, а сам язык странной, тормозной, несерьезной игрушкой, которую солидные люди вообще отключают в своих браузерах. Но из-за его монополии все равно кто-то должен был на нем писать, а чтобы продолжать этим заниматься и не страдать от чувства собственной неполноценности, приходилось как-то компенсировать, поддерживать друг друга душеспасательными статьями, сопровождаемыми их картинками с логотипом JS на фоне карты мира, конференциями и т. д. Думаете, зачем кому-то понадобилось программировать на JS микроконтроллеры? Все для этого же. «Они думают, мой язык несерьезный, ну тогда я назло им буду использовать его везде, где только возможно, в самых неочевидных местах». И я не буду скрывать, я действительно ощущаю при мыслях об этом тепло во всем теле…


    1. pnovikov Автор
      08.08.2017 13:48
      +1

      Можно сказать, что ради такого комментария я и писал статью.


    1. jetexe
      08.08.2017 14:19

      А что тогда делать PHP разработчикам? Нас и по сей день считают людьми третьего сорта. Но мы же не лезем на фронтэнд и МК.


  1. wert_lex
    08.08.2017 16:00
    +1

    Не кидайте помидорами, но мне кажется, что сегодняшний JavaScript ближе к Java чем когда бы то ни было. Смотрите сами:


    1. Кроссплатформенность. Java на кофеварке сегодня запустить не так уж и прямо (профили, вот это всё), но браузер там скорее всего есть. И с помощью нескучного тюнинга бабеля, думаю можно под этот браузер писать со всеми радостями ES7+.
    2. Write Once Run Everywhere. Опять же за счёт бабеля (по желанию можно взять что-то другое) веб-приложение может быть собрано в какой-нибудь ES3 и запуститься на старых платформах.
    3. Язык/Платформа. Сам по себе язык довольно простой с несколькими неочевидными нюансами, которые спустя пару недель проклятий становятся уже не настолько неочевидными. Платформа, конечно, так себе — реализации отличаются, стандартная библиотека очень маленькая, но тем не менее.

    М? Чем не каноничная Java? Осталось портировать Spring, чтобы было для чего конфиги иксэмэльные писать, и готово :)


  1. iCoderXXI
    09.08.2017 00:53
    +3

    Первые несколько лет жабаскрипт (es3) меня дико кошмарил. После «нормальных» ЯП типа PHP4, Clipper 5.2, Borland Pascal 7 он казался каким-то уродским уродом.

    Тем не менее жизнь сказала «НАДО!» и никуда не денешься.

    Потом во фронтенд бодро ворвался jQuery и жить стало неимоверно легче, кошмары закончились.

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

    Потом JavaScript внезапно начал нравиться, особенно после курсов зоракса и JavaScript Weird Parts. В голове наступило просветление.

    А сейчас мне уже становится сложновато писать на PHP, потому что остро не хватает удобств JS…

    В общем я безнадёжен.

    P.S.: И да, куда ж мы без ноды и реакта… :)


  1. metalim
    11.08.2017 00:01
    -2

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


  1. dcc0
    12.08.2017 19:06
    -1

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

    534 other ways to reload the page with JavaScript
    https://stackoverflow.com/questions/3715047/how-to-reload-a-page-using-javascript/43064234#43064234


    1. vintage
      12.08.2017 19:32
      +2

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


    1. mayorovp
      12.08.2017 20:07

      Там основных способов не так много — всего пять, большинство строк — просто игры с синтаксисом языка.


    1. pnovikov Автор
      13.08.2017 09:00
      +2

      От перестановки self, location, reload местами, результат не меняется :D