Зачем асинхронность JavaScript, когда уже есть WebWorkers?
От: konsoletyper Россия https://github.com/konsoletyper
Дата: 26.05.14 08:36
Оценка: +1 -2
Долгое время JavaScript был однопоточным, причём тем самым одним потоком был поток UI. Это заставляло разработчиков браузеров включать асинхронные API, чтобы UI не повисал, пока скрипты ждут отклика от файловой системы или сети. Тем не менее, всё поменялось с приходом WebWorkers, которые могут сколь угодно долго работать в фоне, не заставляя UI впадать в ступор. Так многие делают вне мира JavaScript, и никому не надо страдать из-за колбэков.

К сожалению, новые появляющиеся API для JavaScript игнорируют наличие WebWorker'ов и по-старинке делают работу асинхронно. Например, есть IndexedDB по спецификации имеет две версии API, причём в браузерах реализована только асинхронная версия. Для WebSocket синхронной версии нет даже в проекте.

Чем же меня не устраивают асинхронные API? Функция, которая должна вызывать асинхронный API, должна так же быть асинхронной. В следствие этого другая функция, которая вызывает её, так же должна быть асинхронной, и так далее по всему стеку вызовов. Итак, одна асинхронная функция заражает асинхронностью почти всю программу. Люди говорят, что для борьбы с колбэками есть разные вещи, есть promises (которые, ИМХО, никак не помогают), есть streamline.js, который помогает, но не решает другой проблемы с колбэками — их низкой производительсноти.

Я понимаю, что вызов колбэков — это хорошо оптимизированная штука, которая сама по себе ничего не стоит. С другой стороны, как вообще работают оптимизаторы? Например, одна из их обязанностей — вынос инвариантов цикла за рамки этого цикла (т.н. loop invariant motion). Когда у нас есть цельная функция, есть и цельный граф потока управления (CFG), в котором циклы собственно и будут циклами, а инварианты найти тоже не проблема. Когда функция распадается на множество кусочков, каждый со своим простым CFG, никаких подобных оптимизаций произвести не удастся. Плюс, разумеется, для continuation-passing-style необходимы накладные расходы на сохранение контекста.

Вот пример, с которым лично я столкнулся. Я пытаюсь реализовать оффлайновый поиск маршрута в OpenStreetMap. Он работает нормально, пока данные в памяти. Но если хочется искать на большей площади, приходится просить больше памяти, поэтому есть идея хранить данные на диски и подгружать их по требованию. К сожалению, синхронная IndexedDB не реализована, а для асинхронной придётся переписывать в асинхронном стиле алгоритм Дийксты с Contraction Hierarchies, и с деревом поиска для нахождения ближайшей ноды к заданным координатам. В общем, убиться можно. Но допустим я возьму и перепишу. Как изменится производительность? JavaScript и так неторопливо работает, а будучи преобразованной в CPS-стиль, начнёт работать вообще невообразимо медленно.

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

Так почему же разработчики браузеров до сих пор игнорируют присутствие WebWorker'ов и не включают синхронные API, которые очень даже нужные для сложных, производительных оффлайн-приложений? С другой стороны, а если сложные, производительные оффлайн-приложения не нужны, для чего тогда вообще нагородили всяких этих WebWorker'ов и IndexedDB?
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.