Как перестать отлаживать асинхронный код и начать жить

в 22:10, , рубрики: frontendconf, javascript, Schlecht!Script, андрей саломатин, Блог компании Конференции Олега Бунина (Онтико), параллельное программирование, Программирование, метки: , ,

Андрей Саломатин ( filipovskii_off)

Андрей Саломатин

Сегодня каждый день появляются новые языки программирования — Go, Rust, CoffeeScript — все, что угодно. Я решил, что я тоже горазд придумать свой язык программирования, что миру не хватает какого-то нового языка…

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

Что в нем не очень обычно, что может даже оттолкнуть, на первый взгляд, — это то, что в Schlecht!Script функции имеют цвет.

Как перестать отлаживать асинхронный код и начать жить - 2

Т.е., когда вы объявляете функцию, когда вы ее вызываете, вы явным образом указываете ее цвет.

Функции бывают красные и синие — двух цветов.

Важный момент: внутри синих функций вы можете вызывать только другие синие функции. Вы не можете вызывать красные функции внутри синих функций.

Как перестать отлаживать асинхронный код и начать жить - 3

Внутри красных функций вы можете вызывать и красные, и синие функции.

Как перестать отлаживать асинхронный код и начать жить - 4

Я решил, что должно быть так. В каждом языке должно быть так.

Тонкий момент: красные функции писать и вызывать больно! Что я имею в виду, когда говорю «больно»? Дело в том, что сейчас я изучаю немецкий язык, и я решил, что красные функции мы должны все называть только на немецком языке, иначе интерпретатор просто не поймет, что вы ему пытаетесь впихнуть, и он просто не будет это выполнять.

Как перестать отлаживать асинхронный код и начать жить - 5

Вот так вы должны писать функции на немецком языке:

Как перестать отлаживать асинхронный код и начать жить - 6

«!» обязателен — мы же на немецком пишем, в конце концов.

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

Вопрос к вам — стали бы вы использовать такой язык? Продал ли я вам Schlecht!Script?

Ну, у вас, как бы, нет выбора. Простите…

JavaScript.

JavaScript — отличный язык, мы все его любим, мы все здесь собрались, потому что мы любим JavaScript. Но проблема в том, что JavaScript наследует некоторые черты Schlecht!Script, и я, конечно, не хочу хвастаться, но, по-моему, они украли пару моих идей.

Что именно они наследуют? в JavaScript есть красные и синие функции. Красные функции в JavaScript — это асинхронные функции, синие — синхронные функции. И все прослеживается, все та же цепочка… Красные функции вызывать больно в Schlecht!Script, а асинхронные функции вызывать больно в JavaScript.

И внутри синих функций мы не можем писать красные функции. Я еще скажу об этом позже.

Как перестать отлаживать асинхронный код и начать жить - 7

Почему это больно? Откуда боль при вызове и при написании асинхронных функций?

У нас по-другому работают условные операторы, циклы, return. У нас не работает try/catch, и асинхронные функции ломают абстракцию.

О каждом пункте немного подробнее.

Как перестать отлаживать асинхронный код и начать жить - 8

Вот так выглядит синхронный код, где shouldProcess и process — это функции синхронные, и работают условные операторы, работает for, в общем, все хорошо.

То же самое, но асинхронное, будет выглядеть вот так вот:

Как перестать отлаживать асинхронный код и начать жить - 9

Там появилась рекурсия, мы передаем состояние в параметры, в функцию. В общем, смотреть прямо-таки неприятно. У нас не работает try/catch и, я думаю, мы все знаем, что если мы обернем синхронный блок кода в try/catch, исключения мы не поймаем. Нам нужно будет передать callback, перенавесить обработчик событий, в общем, у нас нет try/catch…

И асинхронные функции ломают абстракцию. Что я имею в виду? Представьте, что вы написали кэш. Вы сделали кэш пользователей в памяти. И у вас есть функция, которая читает из этого кэша, которая, естественно, синхронная, потому что все в памяти. Завтра к вам приходят тысячи, миллионы, миллиарды пользователей, и вам нужно положить этот кэш в Redis. Вы кладете кэш в Redis, функция становится асинхронной, потому что из-за Redis'а мы можем читать только асинхронно. И, соответственно, весь стек, который вызывал вашу синхронную функцию, придется переписать, потому что теперь весь стек становится асинхронным. Если какая-то функция зависела от функции чтения из кэша, она так же будет теперь асинхронной.

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

Но что мы все об асинхронности? Давайте немного обо мне поговорим, наконец.

Как перестать отлаживать асинхронный код и начать жить - 10

Я пришел, чтобы вас всех спасти. Ну, я попробую это сделать.

Меня зовут Андрей, я работаю в стартапе «Productive Mobile» в Берлине. Я помогаю с организацией MoscowJS и я являюсь соведущим RadioJS. Мне очень интересна тема асинхронности и не только в JavaScript, я считаю, что, в принципе, это определяющий момент языка. То, как язык работает с асинхронностью, определяет его успех и то, насколько людям приятно и удобно с ним работать.

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

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

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

И, говоря об этих двух сценариях, интересно порассуждать: вот, типа, асинхронность — плохо, в общем, все грустно… А что мы на самом деле хотим? Как бы выглядел идеальный асинхронный код?

Как перестать отлаживать асинхронный код и начать жить - 11

А хотим мы, мне кажется, контроля потока управления. Мы хотим, чтобы наши условные операторы, циклы работали в синхронном коде так же, как в асинхронном.

Мы хотим обработки исключений. Зачем нам try/catch, если мы не можем его использовать в асинхронных операциях? Это просто странно.

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

Вот, чего мы хотим.

А что у нас есть сегодня, и какие инструменты у нас появятся в будущем?

Как перестать отлаживать асинхронный код и начать жить - 12

Если мы говорим об ECMAScript 6 (это, в принципе, то, о чем я буду говорить сегодня), для работы с множеством событий у нас есть EventEmitter и Stream, а для работы с единичными асинхронными операциями — Continuation Passing Style (они же callback'и), Promises и Coroutines.

Как перестать отлаживать асинхронный код и начать жить - 13

В ECMAScript 7 у нас появятся Async Generators для работы с множеством событий и Async/Await — для работы с единичными асинхронными операциями.

Об этом и поговорим.

Начнем с того, что у нас есть в ECMAScript 6 для работы с множеством асинхронных событий. Напомню, например, это обработка событий мыши или нажатий на клавиши. У нас есть паттерн EventEmitter, который реализован в браузере в Node.js. Он встречается практически в любой API, где мы работаем с множеством событий. EventEmitter говорит нам, что мы можем создать объект, который излучает события, и навешивать обработчики на каждый тип события.

Как перестать отлаживать асинхронный код и начать жить - 14

Интерфейс очень простой. Мы можем добавлять EventListener, убирать EventListener по названию event'а, передавая туда сallback.

Как перестать отлаживать асинхронный код и начать жить - 15

К примеру, в XMLHttpRequest, когда я говорю о множестве событий, я имею в виду, что у нас может быть множество событий progress. Т.е. по мере того, как мы загружаем какие-то данные с помощью AJAX-запроса, нам выстреливают события progress, и по одному разу выстреливают события load, abort и error:

Как перестать отлаживать асинхронный код и начать жить - 16

Error — это особенное событие, универсальное событие в EventEmitter'ах и в Stream'ах для того чтобы уведомить пользователя об ошибке.

Есть множество реализаций:

Как перестать отлаживать асинхронный код и начать жить - 17

Здесь перечислены только несколько, и в конце доклада будет ссылка, где все эти реализации есть.

Важно сказать, что в Node.js EventEmitter встроен по умолчанию.

Итак, это то, что у нас есть практически по стандарту в API и браузерах в Node.js.

Что у нас еще есть для работы с множеством событий? Stream.

Stream — это поток данных. Что такое данные? Это могут быть бинарные данные, например, данные из файла, текстовые данные, либо объекты или события. Самые популярные примеры:

Как перестать отлаживать асинхронный код и начать жить - 18

Есть несколько типов потоков:

Как перестать отлаживать асинхронный код и начать жить - 19

Здесь мы рассматриваем цепочку преобразований из Stylus файлов в css файлы, добавляя автопрефиксер, потому что все мы любим Андрея Ситника и его автопрефиксер.

Вы видите, что у нас есть несколько типов потоков — поток-источник это gulp.src, который читает файлы и излучает объекты файлы, которые потом идут в потоки преобразования. Первый поток преобразования делает из stylus файла css, второй поток преобразования добавляет префиксы. И последний тип потоков — это поток-потребитель, который принимает эти объекты, пишет что-то куда-то на диск, и ничего не излучает.

Как перестать отлаживать асинхронный код и начать жить - 20

Т.е. у нас есть 3 типа потоков — источник данных, преобразование и потребитель. И эти паттерны прослеживаются везде, не только в gulp, но и при наблюдении за DOM событиями. У нас есть потоки, которые излучают DOM-события, которые их преобразуют и что-то, что потребляя эти DOM-события, возвращает конкретный результат.

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

Есть несколько реализаций потоков, или они же Observables:

Как перестать отлаживать асинхронный код и начать жить - 21

В Node.js потоки встроенные — это Node Streams.

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

Как перестать отлаживать асинхронный код и начать жить - 22

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

В общем, для работы с множеством событий в ECMAScript 6 все не очень хорошо.

Когда мы говорим об единичных асинхронных операциях, у нас есть 3 подхода в ECMAScript 6:

Как перестать отлаживать асинхронный код и начать жить - 23

Continuation Passing Style, они же — callback'и.

Как перестать отлаживать асинхронный код и начать жить - 24

Я думаю, вы все к этому уже привыкли. Это когда мы делаем асинхронный запрос, передаем туда callback, и callback будет вызван либо с ошибкой, либо с результатом. Это распространенный подход, он есть и в браузере в Node.

Как перестать отлаживать асинхронный код и начать жить - 25

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

Как перестать отлаживать асинхронный код и начать жить - 26

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

Как перестать отлаживать асинхронный код и начать жить - 27

Тот же самый код, но синхронно, выглядит следующим образом:

Как перестать отлаживать асинхронный код и начать жить - 28

Можно увеличить шрифт, чтобы было виднее. И еще немного увеличить… И мы прекрасно понимаем, что это Schlecht!Script.

Как перестать отлаживать асинхронный код и начать жить - 29

Я говорил, что они украли мою идею.

Continuation Passing Style — это стандартный API в браузере в Node.js, мы работаем с ними постоянно, но это неудобно. Поэтому у нас есть Promises. Это объект, который представляет собой асинхронную операцию.

Как перестать отлаживать асинхронный код и начать жить - 30

Promise — это как бы обещание что-то сделать в будущем, асинхронно.

Как перестать отлаживать асинхронный код и начать жить - 31

Мы можем навешивать callback'и на асинхронную операцию с помощью метода then, и это, в принципе, основной метод в API. И мы можем, очень важно, чейнить Promises, мы можем вызывать then последовательно, и каждая функция, которая передается в then, также может возвращать Promises. Вот так выглядит запрос ленты пользователя из твиттера на Promises.

Если мы сравниваем этот подход с Continuation Passing Style, Promises, естественно, удобнее пользоваться — они дают нам возможность писать гораздо меньше бойлерплейта.

Continuation Passing Style, по-прежнему, используется во всех API по умолчанию, в Node.js, в io.js., и они даже не планируют переходы на Promises по нескольким причинам. Сначала многие говорили, что причины — это производительность. И это действительно так, исследования 2013 года показывают, что Promises сильно позади callback'ов. Но с появлением таких библиотек как bluebird, мы уже можем уверенно сказать, что это не так, потому что Promises в bluebird приближаются в производительности к callback'ам. Важный момент: почему Promises не рекомендуют использовать в API до сих пор? Потому что, когда вы выдаете Promises из вашей API, вы навязываете имплементацию.

Все Promises библиотеки должны подчиняться стандарту, но выдавая Promises, вы так же выдаете и имплементацию, т.е. если вы написали свой код, используя медленные Promises, и выдаете медленный Promises из API, это будет не очень приятно пользователям. Поэтому для внешних API, конечно же, по-прежнему рекомендуют использовать callback'и.

Как перестать отлаживать асинхронный код и начать жить - 32

Реализаций Promises — масса, и если вы не написали свою реализацию, вы ненастоящий JavaScript-программист. Я не написал свою реализацию Promises, поэтому мне пришлось придумать свой язык.

Итак, Promises, в общем, чуть меньше бойлерплейта, но, тем не менее, все еще не так хорошо.

Что по поводу Coroutines? Здесь уже начинаются интересные штуки. Представьте…

Это интересно. Мы были на JSConf в Будапеште, и там был сумасшедший человек, который на JavaScript программировал квадракоптер и что-то еще, и половину из того, что он пытался нам показать, у него не получалось. Поэтому он постоянно говорил: «OK, теперь представьте… Этот квадракоптер взлетел и все получилось...».

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

Как перестать отлаживать асинхронный код и начать жить - 33

Здесь функция получает имя пользователя, она лезет в базу, получает объект пользователя, возвращает его имя. Естественно, «залезть в базу» — функция getUser асинхронная. Что, если мы могли бы поставить функцию getUserName на паузу в момент вызова getUser? Вот, мы выполняем нашу getUserName функцию, дошли до getUser и остановились. getUser сходил в базу, получил объект, вернул его в функцию, мы продолжаем выполнение. Насколько круто это было бы.

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

Как перестать отлаживать асинхронный код и начать жить - 34

Это не блокирующая операция. Мы останавливаем выполнение конкретной функции в конкретном месте.

Как перестать отлаживать асинхронный код и начать жить - 35

Как getUserName выглядит с помощью генераторов на JavaScript? Нам нужно добавить «*» в объявление функции, чтобы сказать о том, что функция возвращает генератор. Мы можем использовать ключевое слово «yield» в том месте, где мы хотим поставить функцию на паузу. И важно помнить, что getUser здесь возвращает Promises.

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

Как перестать отлаживать асинхронный код и начать жить - 36

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

Итого, вот, что у нас получается:

Как перестать отлаживать асинхронный код и начать жить - 37

У нас функция, внутри которой мы можем использовать if, for и другие операторы.

Чтобы вернуть значение, мы просто пишем return, так же, как в синхронной функции. Мы можем использовать try/catch внутри, и мы поймаем исключение.

Как перестать отлаживать асинхронный код и начать жить - 38

Если Promises с getUser разрешится с ошибкой, это выкинется как исключение.

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

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

Как перестать отлаживать асинхронный код и начать жить - 39

Есть множество реализаций. Какие-то используют генераторы, которые уже часть стандарта, есть Fibers, которые в Node.js работают и которые не используют генератор, а свои заморочки у них.

В общем, это третий подход для работы с единичными асинхронными операциями, и это пока еще хак, но мы уже можем использовать код, который приближен к синхронному. Мы можем использовать условные операторы, циклы и блоки try/catch.

Как перестать отлаживать асинхронный код и начать жить - 40

Т.е. ECMAScript 6 для работы с единичными асинхронными операциями как бы немного приближает нас к желаемому результату, но по-прежнему проблема единого интерфейса не решена, даже в Coroutines, потому что нам нужно писать «*» специальную и использовать ключевой оператор «yield».

Как перестать отлаживать асинхронный код и начать жить - 41

Итак, в ECMAScript 6 для работы с множеством событий у нас есть EventEmitter и Stream, для работы с единичными асинхронными операциями — CPS, Promises, Coroutines. И все это, вроде бы, здорово, но чего-то не хватает. Хочется большего, чего-то отважного, смелого, нового, хочется революции.

И ребята, которые пишут ES7, решили дать нам революцию и принесли для нас Async/Await и Async Generators.

Как перестать отлаживать асинхронный код и начать жить - 42

Async/Await — это стандарт, который позволяет нам работать с единичными асинхронными операциями, такими, как, например, запросы в БД.

Вот так мы писали getUserName на генераторах:

Как перестать отлаживать асинхронный код и начать жить - 43

А вот так тот же код выглядит с помощью Async/Await:

Как перестать отлаживать асинхронный код и начать жить - 44

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

Как перестать отлаживать асинхронный код и начать жить - 45

И так же у нас работают условные операторы, циклы, и try/catch, то бишь, асинхронные функции легализованы в ES7. Теперь мы явно говорим, что если функция асинхронная, то добавьте ключевое слово «async». И это, в принципе, не так плохо, но опять же единого интерфейса у нас нет.

Что по поводу множества событий? Здесь у нас есть стандарт, который называется Async Generators.

Что такое, вообще, множество? Как мы работаем с множеством в JavaScript?

Как перестать отлаживать асинхронный код и начать жить - 46

C множеством мы работаем при помощи циклов, так давайте работать с множеством событий при помощи циклов.

Как перестать отлаживать асинхронный код и начать жить - 47

Внутри асинхронной функции мы можем использовать ключевую конструкцию «for… on», которая нам позволяет итерировать по асинхронным коллекциям. Как бы.

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

Как перестать отлаживать асинхронный код и начать жить - 48

Т.к. функция асинхронная, важно понимать, что она возвращает Promise. Но что, если мы хотим вернуть множество значений, если мы хотим, например, обрабатывать как-то сообщения из веб-сокета, фильтровать их? Т.е. у нас поступает множество и на выходе у нас множество. Здесь нам помогают асинхронные генераторы. Мы пишем «async function *» и говорим о том, что функция асинхронна, и мы возвращаем множество какое-то.

Как перестать отлаживать асинхронный код и начать жить - 49

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

Как перестать отлаживать асинхронный код и начать жить - 50

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

Вопрос: что возвращает filterWSMessages?

Как перестать отлаживать асинхронный код и начать жить - 51

Это точно не Promise, потому что это какая-то коллекция, что-то в этом роде… Но это и не массив.

Как перестать отлаживать асинхронный код и начать жить - 52

Даже больше. Что возвращают эти Observe, которые генерят события?

А возвращают они т.н. объекты Observables. Это новое слово, но по большому счету, Observables — это потоки, это Streams. Т.о., круг замыкается.

Итого, у нас есть для работы с асинхронными единичными операциями Async/Await, для работы с множеством — Async Generators.

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

Чтобы получить ленту твитов, в CPS мы писали бы вот такой код:

Как перестать отлаживать асинхронный код и начать жить - 53

Много бойлерплейта, обработка ошибок, ручная практически и, в общем, не очень приятная.

С помощью Promise код выглядит таким вот образом:

Как перестать отлаживать асинхронный код и начать жить - 54

Бойлерплейт поменьше, мы можем обрабатывать исключения в одном месте, что уже хорошо, но, тем не менее, есть эти then..., не работают ни try/ catch, ни условные операторы.

С помощью Async/Await получаем такую конструкцию:

Как перестать отлаживать асинхронный код и начать жить - 55

И примерно то же нам дают Coroutines.

Здесь все шикарно, за исключением того, что нам нужно объявить эту функцию как «async».

Что касается множества событий, если мы говорим о DOM-событиях, вот так мы обрабатывали бы mousemove и рисовали бы по экрану с помощью EventEmitter'а:

Как перестать отлаживать асинхронный код и начать жить - 56

Тот же самый код, но с помощью Stream'ов и библиотеки Kefir выглядит таким образом:

Как перестать отлаживать асинхронный код и начать жить - 57

Мы создаем поток из событий mousemove на window, мы их каким-то образом фильтруем, и на каждое значение мы вызываем callback функцию. И, когда мы вызовем end у этого stream'а, мы автоматически отпишемся от событий в DOM, что немаловажно

Async Generators выглядит таким образом:

Как перестать отлаживать асинхронный код и начать жить - 58

Это просто цикл, мы итерируем по коллекции асинхронных событий и производим какие-то операции над ними.

По-моему, это огромный путь.

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

  • Определите вашу задачу, т.е. если вы работаете с множеством событий, имеет смысл посмотреть на Streams или, возможно даже, на Async Generators, если у вас есть транспайлер.
  • Если вы работаете с базой, например, отправляете туда запросы или отправляете AJAX-запросы, которые могут либо зафейлиться, либо выполниться, используйте Promises.
  • Обдумайте ваши ограничения. Если вы можете использовать транспайлер, имеет смысл посмотреть на Async/Await и Async Generators. Если вы пишете API, возможно, не имеет смысла экспоузить Promise в качестве внешней API и делать все на callback’ах.
  • Используйте лучшие практики, помните про события error на потоках и на EventEmitter'ах.
  • Помните про специальные методы вроде Promise.all и т.д.

Я знаю, всех вас интересует судьба Schlecht!Script, когда он будет выложен на GitHub и т.д., но дело в том, что из-за постоянной критики, обвинений в плагиате — говорят, что язык такой уже есть, ничего нового я не изобрел, я решил закрыть проект и посвятить себя, может быть, чему-то полезному, важному, интересному, возможно, я даже напишу свою библиотеку Promises.

Контакты

» twitter
» filipovskii_off

Этот доклад — расшифровка одного из лучших выступлений на конференции фронтенд-разработчиков FrontendConf. Мы уже открыли приём докладов на 2017 год.

На главной странице Frontend Conf, кстати, есть форма подписки на лучшие материалы конференции. Подпишитесь, минимум 8 лучших расшифровок, гарантированы.

А на ближайшей HighLoad++ уже 11 заявок по теме «Производительность фронтенда», например:

Автор:

Источник


* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js