← На главную

Почему async/await не решает проблему красных и синих функций в JS

26.05.2026 15:58 · hackernews

Автор придумывает вымышленный язык, похожий на JavaScript, с одним безумным правилом: у каждой функции есть цвет. Либо красный, либо синий. Вызывать функцию можно только синтаксисом, соответствующим её цвету. И главная боль: красную функцию можно вызвать только из другой красной. Синяя — из любой. Но сами красные функции вызывать больнее — то verbose, то с ограничениями.

Казалось бы, делай всё синим и живи спокойно. Но авторы языка засунули в стандартную библиотеку красные функции, без которых не обойтись. Теперь любая функция, которая их использует, тоже становится красной. И эта краснота ползёт вверх по стеку, окрашивая всё на своём пути. Высший порядок? Забудь. Разбил код на переиспользуемые функции — думай о цвете.

Это аллегория. Красные функции — асинхронные. В JavaScript на Node.js любая функция, принимающая колбэк, — красная. Синхронные возвращают значение, асинхронные — нет, их результат получаешь через callback. Синхронный код не может дождаться асинхронного. Асинхронщина не встраивается в выражения, плохо дружит с try/catch и циклами. Всё это и есть «callback hell».

Промисы (promises, они же futures) чуть смягчают боль, но проблему не решают. Мир всё ещё разделён на два цвета. Async/await в C#, Dart, Python — шаг вперёд. Он заставляет компилятор сам делать CPS-трансформацию (разбивать функцию на куски и превращать их в цепочку замыканий). Это избавляет от синтаксической боли. Но цвет остаётся: синхронные функции возвращают значение, асинхронные — Task<T> или Future<T>, и вызвать их можно только с await. Разделение никуда не делось.

Какие языки этой проблемы лишены? Go. Lua. Ruby. И старый добрый Java до того, как в него начали пихать фьючерсы. Что у них общего? Потоки. Не обязательно системные — горутины в Go, корутины в Lua, файберы в Ruby. Они позволяют приостановить весь стек вызовов, а не разматывать его до event loop’а. В Go IO-операции в стандартной библиотеке выглядят синхронно — просто делают работу и возвращают результат. Но параллельно могут выполняться другие горутины. Go просто уничтожает разницу между синхронным и асинхронным кодом. Конкурентность становится вопросом моделирования программы, а не цветом, вклеенным в каждую функцию. Поэтому когда кто-то хвалит новый язык за «клёвые асинхронные API», автор скрежещет зубами — это значит, что язык снова разделил функции на красные и синие.

Читать оригинал →