Почему JavaScript не использует собственный поток в общих браузерах?

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

Почему это?

По моему опыту можно было получить чрезвычайно улучшенный пользовательский интерфейс, если бы JavaScript работал в своем собственном потоке, отдельно JS не блокировал визуализацию пользовательского интерфейса или освобождение сложного или ограниченного шаблона оптимизации очереди сообщений (да, также вы, веб-работники!), который разработчик должен написать себя, чтобы поддерживать пользовательский интерфейс во всем, когда он действительно подходит к нему.

Я заинтересован в понимании мотивации, которая управляет таким, казалось бы, неудачным дизайнерским решением, есть ли убедительная причина с точки зрения архитектуры программного обеспечения?

Ответы

Ответ 1

Действия пользователя требуют участия от обработчиков событий JS

Пользовательские действия могут инициировать события Javascript (клики, фокус-события, ключевые события и т.д.), которые участвуют и потенциально влияют на действие пользователя, поэтому ясно, что один поток JS не может выполняться во время обработки действий пользователя, если это так, то поток JS не мог участвовать в действиях пользователя, потому что он уже делает что-то еще. Таким образом, браузер не обрабатывает действия пользователя по умолчанию, пока поток JS не будет доступен для участия в этом процессе.

Rendering

Рендеринг более сложный. Типичная последовательность модификации DOM выглядит следующим образом: 1) DOM, измененный JS, макет помечен грязным, 2) поток JS завершает выполнение, поэтому браузер теперь знает, что JS завершил модификацию DOM, 3) браузер делает макет для ретрансляции измененных DOM, 4 ) Браузер окрашивает экран по мере необходимости.

Шаг 2). Если браузер выполнил новый макет и рисунок экрана после каждой модификации JS DOM, весь процесс мог бы быть невероятно неэффективным, если бы JS собирался сделать кучу модификаций DOM. Кроме того, будут проблемы с синхронизацией потоков, поскольку если бы у вас была JS-модификация DOM одновременно с тем, как браузер пытался выполнить ретрансляцию и перерисовку, вам придется синхронизировать эту активность (например, заблокировать кого-то, чтобы операция могла завершиться без базовые данные изменяются другим потоком).

FYI, есть некоторые обходы, которые могут быть использованы для принудительного ретрансляции или для принудительной перерисовки из вашего JS-кода (не совсем то, что вы просили, но полезно в некоторых случаях).

Несколько потоков, обращающихся к DOM Really Complex

DOM по существу является большой общей структурой данных. Браузер строит его, когда страница анализируется. Тогда загрузка скриптов и различных событий JS имеет возможность изменить его.

Если у вас внезапно возникло несколько потоков JS с одновременным доступом к DOM, у вас возникнет очень сложная проблема. Как синхронизировать доступ? Вы даже не могли написать самую основную операцию DOM, которая включала бы поиск объекта DOM на странице, а затем его изменение, потому что это не была бы атомная операция. DOM может быть изменен между моментом, когда вы нашли объект DOM, и когда вы внесли изменения. Вместо этого вам, вероятно, придется приобрести блокировку, по крайней мере, поддерева в DOM, чтобы он не был изменен каким-либо другим потоком, пока вы манипулировали или искали его. Затем, после внесения изменений, вам придется освободить блокировку и освободить любое знание состояния DOM от вашего кода (потому что как только вы отпустите блокировку, какой-то другой поток может ее изменить). И, если вы не сделали что-то правильно, вы могли бы столкнуться с тупиками или всякими неприятными ошибками. На самом деле вам придется рассматривать DOM как параллельное многопользовательское хранилище данных. Это будет значительно более сложная модель программирования.

Избежать сложности

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

Одна из причин, по которой браузер Javascript является успешной платформой, - это то, что он очень доступен для всех уровней разработчиков, и относительно легко учиться и писать сплошной код. В то время как браузер JS может получить более продвинутые функции с течением времени (например, мы получили WebWorkers), вы можете быть абсолютно уверены, что это будет сделано так, чтобы простые вещи оставались простыми, в то время как более продвинутые вещи могут быть сделаны более продвинутыми разработчиками, но без нарушая любые вещи, которые теперь все упрощают.

FYI, я написал многопользовательское веб-серверное приложение в node.js, и я постоянно удивляюсь, насколько менее сложная большая часть дизайна сервера связана с однопоточным характером javascript nodejs. Да, есть несколько вещей, которые больше больно писать (изучите promises для написания большого количества асинхронного кода), но вау упростительное предположение о том, что ваш JS-код никогда не прерывается другим запросом, значительно упрощает дизайн, тестирование и уменьшает сложность поиска и исправления ошибок, которые concurrency дизайн и кодирование всегда чреваты.

Обсуждение

Конечно, первая проблема может быть решена путем разрешения обработчиков событий пользовательских действий в их собственном потоке, чтобы они могли возникать в любое время. Но тогда у вас сразу есть многопоточный Javascript и теперь требуется целая новая инфраструктура JS для синхронизации потоков и целых новых классов ошибок. Дизайнеры браузера Javascript последовательно решили не открывать этот блок.

Проблема с рендерингом может быть улучшена, если это необходимо, но при значительном усложнении кода браузера. Вам нужно было бы придумать какой-то способ угадать, когда работает JS-код, похоже, что он больше не меняет DOM (возможно, некоторое количество мс идет без изменений), потому что вам нужно избегать сразу же делать ретрансляцию и краску экрана каждое изменение DOM. Если бы браузер сделал это, некоторые операции JS стали бы на 100 раз медленнее, чем сегодня (100x - это дикая догадка, но дело в том, что они будут намного медленнее). И вам придется внедрить синхронизацию потоков между макетами, покраской и модификациями JS DOM, которые можно выполнить, но сложной, большой работой и плодотворной почвой для ошибок браузера. И вам нужно решить, что делать, если вы частично используете ретрансляцию или перерисовку, а поток JS делает модификацию DOM (ни один из ответов не замечателен).