LINUX.ORG.RU

node.js-like vs continuations


1

2

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

Собственно, полагаю, что это всё в каком-нибудь racket-е уже есть, но как-то немодно. Про нод жс в каждом туалете говорят.

В чем причина? continuations непригодны для реального мира? Нет языка с джавоподобным синтаксисом с нормальной поддержкой продолжений?

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

★★★★★

node.js непригоден для действительно высоконагруженных серверных проектов. это мое мнение. никогда его не использовал.
Сами знаете какой язык(и?) годны для этих целей.
А хипсторы-петушки пускай выбирают эту профанацию.

nerdogeek
()

Используй либы вроде asyncjs и получай код без лапши. Но мой совет - забудь о nodejs, на данный момент там абсолютно не решены фундаментальные проблемы. Например, на ровном месте может произойти утечка ресурсов, если в цепочке колбеков будет брошено исключение. Когда я об этом написал ребятам из nodejs они сказали мне «просто не используй исключения». Но как их не использовать, если они в стандарте ECMAScript?

tff
()

Ты внезапно изобрел один частный случай монады. У нас так все приложение написано

vertexua ★★★★★
()
Последнее исправление: vertexua (всего исправлений: 1)

Нет языка с джавоподобным синтаксисом с нормальной поддержкой продолжений?

С# 5, async/await

Dantix ★★
()
Ответ на: комментарий от tff

С этой проблемой живет Qt и все приложения Apple. Это не проблема. Писать exception-safe код довольно муторно и проще просто не использовать исключения для рядовых ситуаций.

Legioner ★★★★★
() автор топика
Ответ на: комментарий от Legioner

Сами знаете какой язык(и?) годны для этих целей.

js не годен. Как и любой другой с динамической типизацией. И байткодом.

nerdogeek
()
Ответ на: комментарий от Legioner

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

Dantix ★★
()
Ответ на: комментарий от Legioner

Qt написан на языке, в котором используется RAII для таких случаев. В js такого не провернуть, на данный момент.

tff
()
Ответ на: комментарий от nerdogeek

В этом направлении там нынче усердно пилят typescript.

tff
()
Ответ на: комментарий от tff

Qt написан на языке, в котором используется RAII для таких случаев. В js такого не провернуть, на данный момент.

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

Legioner ★★★★★
() автор топика
Ответ на: комментарий от vertexua

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

Dantix ★★
()
Ответ на: комментарий от vertexua

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

Dantix ★★
()
Ответ на: комментарий от Legioner

Окей, скажу иначе. В C++ можно написать exception safe код, в то время как в js невозможно(можно, но придётся каждый блок в try/catch оборачивать и получать абсолютно не поддерживаемое что-то, еще и с несколькими местами освобождения одного и того же ресурса). То, что разработчики какой-то библиотеки этого не делают - это уже их проблемы. Но из того кода Qt что я видел - там вполне годно всё было, хотя я просто мельком поглядел, и особо не разбирался.

tff
()
Ответ на: комментарий от Dantix

Да, как раз как в FJP. Суть в FJP что оно не разрывает выполнение, оно выполняет задачи внутри join() другой задачи

vertexua ★★★★★
()
Ответ на: комментарий от Legioner

Не совсем так. Если исключение покинет слот, обработчик события или что-то подобное, то может возникнуть внутренне противоречивое состояние или утечка памяти. Но тут следует добавить, что не понятно где вообще ловить исключение из обработчика асинхронного события. Так что если у тебя весь main не завёрнут в try ... catch, то твоё исключение банально terminate'нет программу.

KblCb ★★★★★
()

На самом деле, если так немножко подумать, то нужны не полные continuations (т.е., escaping continuations), а delimited one-shot continuations, потому что полноценные continuations имеют существенный оверхед и не всегда приятную семантику.

А если еще дальше подумать, то one-shot continations эквивалентны корутинам и легковесным тредам: т.к. one-shot delimited continuation - это контекст исполнения (регистры + стэк).

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

Примеры таких ЯП - go, erlang.

dmitry_vk ★★★
()
Ответ на: комментарий от nerdogeek

node.js непригоден для действительно высоконагруженных серверных проектов.

это мое мнение.

никогда его не использовал.

не читал, но осуждаю

marvin_yorke ★★★
()

Continuations пригодны и даже используются в терпрайзе. На них построен F# async workflow. Там код действительно выглядит как линейный, а по сути весь склеен из лоскутков и продолжений. Так, в свое время на F# здорово было описывать ввод/вывод в сильверлайт, тогда как программисты C# мучались из-за неудобных для их языка асинхронных операций.

dave ★★★★★
()

По-моему, ты сейчас изобрёл монады.

Miguel ★★★★★
()

обычный простой код с поддержкой continuations.

Сильно.

korvin_ ★★★★★
()
Ответ на: комментарий от dmitry_vk

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

Примеры таких ЯП - go, erlang.

Плюсую.

korvin_ ★★★★★
()

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

А как распределить обработку запросов на несколько серверов?

harm
()
Ответ на: комментарий от nerdogeek

js не годен. Как и любой другой с динамической типизацией. И байткодом.

Просто для протокола: у JS нет байткода.

tailgunner ★★★★★
()
Ответ на: комментарий от nerdogeek

node.js непригоден для действительно высоконагруженных серверных проектов.
никогда его не использовал.

ваше мнение особо ценное в данном случае

dib2 ★★★★★
()
Ответ на: комментарий от KblCb

Man future monad. Исключение прерывает всю цепочку, вываливается у того, кто ее начал и он ее обрабатывает, например рендерит специальную html страницу. Так же ка линейно, но асинхронно

vertexua ★★★★★
()
Ответ на: комментарий от harm

Никак, один запрос, одна цепочка, один сервер. Только их много, ядра есть чем занять. Задача - не висеть на блокировках никогда

vertexua ★★★★★
()
Последнее исправление: vertexua (всего исправлений: 1)
Ответ на: комментарий от dib2

Да, я был не прав. Используйте node.js. Особенно для задач перегонки видео из одного формата в другой онлайн для тысяч клиентов одновременно или для задач обработки огромных графов.
Заодно отладите гугловый V8. Всем будет хорошо: и гугол доволен, и хипсторы-разработчики, и даже юзеры.

nerdogeek
()
Ответ на: комментарий от nerdogeek

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

node js отлично подходит. Запустили mencoder и пошли обрабатывать следующий запрос.

для задач обработки огромных графов.

Какие задачи, какие графы?

Legioner ★★★★★
() автор топика
Ответ на: комментарий от tailgunner

Просто для протокола: у JS нет байткода.

Ничего страшного, лучше сразу по синтаксическому дереву выполнять. Если что, крутой JIT от V8 замутит asm код быстрее чем с -Ofast, да.

nerdogeek
()

По-моему, ты изобрел coroutines

buddhist ★★★★★
()
Ответ на: комментарий от Legioner

Запустили mencoder и пошли обрабатывать следующий запрос.

Круто, на каждый запрос делать форк процесса. Не то что *ля с синхронизацией тредов.

nerdogeek
()
Ответ на: комментарий от Legioner

node js отлично подходит для написания примитивных легковесных серверов уровня echo-server и всяких там диспетчерезаций на небольшое число клиентов - для чего есть erlang и всякие zeromq/rabbitmq

nerdogeek
()
Ответ на: комментарий от nerdogeek

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

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

Legioner ★★★★★
() автор топика
Ответ на: комментарий от nerdogeek

node js отлично подходит для написания примитивных легковесных серверов уровня echo-server и всяких там диспетчерезаций на небольшое число клиентов

А в чем проблемы написания непримитивных серверов?

- для чего есть erlang и всякие zeromq/rabbitmq

У JavaScript есть свои преимущества. Одно из весомых - можно использовать много кода одновременно на клиенте и на сервере. В случае с другими языками это в лучшем случае трансляция языка в JS, что выглядит не очень, в худшем случае переписывание одного и того же алгоритма и структур данных и поддержка двух копий.

Legioner ★★★★★
() автор топика
Ответ на: комментарий от Legioner

Очевидно, что должен быть кластер серверов

Так я и говорил о диспетчерезации. Там node.js годен - потому что легко масштабируется. Но опять же, если протокол клиент-сервер позволяет много i/o (по кол-ву операций и по объему самих данных), то начнется жуткий оверхед из-за избыточных копирований и сборки мусора.

nerdogeek
()
Ответ на: комментарий от harm

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

vertexua ★★★★★
()
Ответ на: комментарий от Legioner

На фоне перекодировки видео накладные расходы на форк процесса настолько ничтожны

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

nerdogeek
()
Ответ на: комментарий от nerdogeek

Ага, а я где-то видел реализацию http сервера на bash'е.

Суть в том, что в браузере по факту сейчас выполняется только JavaScript. И rich web applications требуют достаточно объёмного кода. Bash тут не при чём.

Legioner ★★★★★
() автор топика
Ответ на: комментарий от nerdogeek

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

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

Legioner ★★★★★
() автор топика

В Python есть delimited continuations, но в существующих версиях нет удобной возможности их комбинировать между собой. В 3.4 будет yield from, и tulip который будет работать как раз как ты хочешь.

maxcom ★★★★★
()
Ответ на: комментарий от nerdogeek

а еще зацени как все 10000потоков падают от одного SIGSEGV в одном из потоков кода той же libav, и все начинается заново...
10к что процессов что потоков в случае ресурсоемких задач а-ля конвертация видео - нонсенс.

dib2 ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.