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

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


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

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

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

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

2188a9f446f005b40c0ca60f02cc8ad0.png

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

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

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

2da3f14f7a4fd2ca0263d83de3213a98.png

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

de14bb8682222237d754c2936fc7f041.png

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

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

22d2fb9edd17b716a87f67a98f6214a0.png

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

bd73d528d75186b66be37c841d0bf417.png

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

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

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

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


JavaScript.

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

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

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

5f97d9b518254247033060371a4aa63c.png

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

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

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

9ae1fdd368c4f5a4af2102b265c72444.png

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

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

fa2294a9997aabd3eb8da5ef8ac5c0b9.png

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

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

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

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

5718b94b9c13be7f9aed6e58e3b689e2.png

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

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

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

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

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

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

86c635ae6c736459ae82435ce4225d23.png

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

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

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

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

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

221f2de14ce1e75a51203151970e3cc5.png

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

909895e02837a921318eb704eac8179e.png

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

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

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

34f9b81f147c41756ce21942afda3245.png

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

f3ee898b984c23e0a89bb3a64cd18e45.png

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

1b6457e064e88520978f1f829c03eaa4.png

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

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

eef7d0df31b5f18c638463176c6db549.png

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

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

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

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

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

6d41bc2af575f144fd561119330219e5.png

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

fddd724a24063fa26b2c8a6c45a4043c.png

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

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

2dff02a76d0ceb7020a4e823455ccb48.png

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

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

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

a28c81d197202f4b5497b8b668d08fd3.png

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

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

6730732d6f2e375c232d78dff09cf31a.png

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

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

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

a7cbbd2c38cb577a4c12a10a9c389344.png

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

0ae358fb5768b38885ff9db40c82584e.png

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

ca6e16b23df6ac778531dae274ec6f20.png

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

ecdceb513cde73c1c2b1cf71387bbcc7.png

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

966a46dd58f0527047dee967c110338f.png

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

2aa6334fc1f9ae335d524e9b447e7135.png

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

3e6da28ecbae853f6608314023cb2d60.png

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

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

06e37393a308f3d2f70fd9ee9f57b14a.png

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

c9b8ff2f2ce2613d2c712b1340ffde37.png

Мы можем навешивать 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’и.

75532a89a2f64f69dc8fdedd4a8fca50.png

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

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

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

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

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

069dbae163a8ab7627a933afc7356564.png

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

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

9f1a507067ff914ee936b173bca95f1a.png

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

4b40864b27da22f263cfd0d6b8b87cab.png

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

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

1ba2f543d9d839d54cba762f95bba59c.png

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

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

6e42fa67a948388f052c16cc8f49ebb6.png

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

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

8aac785462b1b0d0648da7c676cce870.png

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

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

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

28d45fef486a925ed70d454a027f5e33.png

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

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

f9820de1041f8164fe5074f901dec39a.png

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

749daa42dfd16ec7f869035d3984ad5c.png

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

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

53a1f81c93e944362cf486315b0a7fca.png

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

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

93739424dd4786bce5fad82c8ba34af4.png

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

944ea94967db91a9849ac8ce5fb0d356.png

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

1cbb637fbc474e77857a872e264bafd6.png

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

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

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

d34285cec15e988a5e6f055ae1266ccf.png

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

34cc25a6fb7f75620b426f235439eb09.png

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

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

eb342aa6538ccf591b04881333ef20ac.png

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

31c793d9ce6d6d50db6d2109d842c99d.png

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

a2637a83c617c766d7730d5d26e5f0d9.png

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

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

59ba09c45503c70a66a42d70a4736c7e.png

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

517049508a69598e42e1a25f31925402.png

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

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

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

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

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

f45ea78d12f11e7a12f9ab62eb51fb8e.png

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

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

0d7af166d49c1c22741b4414e629656d.png

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

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

4121448f537d2305c231aafd9df52385.png

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

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

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

7a75862fd1b3ebb8d9267e0cdb9e94fc.png

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

c71b897f2757b736c024813704f9a633.png

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

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

9f3552fdac63a49ca68facb7a26673c3.png

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

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

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

  • Определите вашу задачу, т.е. если вы работаете с множеством событий, имеет смысл посмотреть на 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 заявок по теме «Производительность фронтенда», например:

  • Применяем стандарты кодирования NASA к JavaScript / Денис Радин (Liberty Global);
  • Отрисовать за 16 мс / Глеб Михеев (Beta Digital Production);
  • Порядок для скорости. Система структурирования фронтендовой части веб-приложений / Сергей Гущин (SuperJob);

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

  • 3 октября 2016 в 02:58 (комментарий был изменён)

    +2

    Вообще, хорошим тоном считается ссылаться на первоисточники.
    Впрочем, я допускаю, что цветовая метафора была изобретена вами самостоятельно.
    • 3 октября 2016 в 05:25

      0

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

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

  • 3 октября 2016 в 05:05

    +1

    сейчас 5 утра, чувак, а твоя статья между двух статей с одинаковыми картинками (пруф-картинка), загадывай желание! (извиняюсь за оффтоп)

© Habrahabr.ru