JavaScript Engine Engine Unspecified?

Недавно я начал изучать JavaScript. Я работал над созданием приложений с Node.js и Angular в течение нескольких месяцев.

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

Итак, я нашел пару интересных статей ([1], [2]), в котором объясняется, как я могу гарантировать, что любая часть кода, который я пишу, всегда будет выполняться одним потоком в то время. Итог, весь мой асинхронный код просто планируется выполнить в какой-то момент в цикле событий. Это очень похоже на то, что планировщик ОС работал бы на машине с одним процессором, где каждый процесс планировал использовать процессор в течение ограниченного времени, давая нам поддельный смысл parallelism. И обратные вызовы будут выглядеть как прерывания.

В статьях не приводятся какие-либо конкретные ссылки, поэтому я подумал, что лучший источник того, как работает механизм выполнения JavaScript, безусловно, должен быть спецификацией языка, и поэтому я получил последнюю копию EcmaScript 5.1.

К моему большому удивлению, я обнаружил, что это поведение выполнения не указано там. Как так? Это похоже на фундаментальный выбор дизайна, сделанный во всех механизмах выполнения JavaScript в браузерах и в node. Интересно, что мне не удалось найти место, где это указано для любого конкретного движка. На самом деле, я не знаю, как люди узнают об этом, так это то, как все работает до такой степени, что это категорически подтверждается в книгах и блогах, подобных тем, которые были упомянуты выше.

Итак, у меня есть набор того, что я считаю интересными вопросами. Я был бы признателен за любые ответы, содержащие идеи, замечания или просто ссылки, указывающие мне в правильном направлении, чтобы понять следующее:

  • Так как EcmaScript не указывает, что механизм выполнения JavaScript должен работать с циклом событий, каким образом реализация JavaScript может работать таким образом не только в браузерах, но и в node.js?
  • Означает ли это, что я могу реализовать новый механизм JavaScript, совместимый с EcmaScript, который фактически предоставляет истинные возможности многопоточности с такими функциями, как блокировки синхронизации, условия и т.д.
  • Эта модель выполнения с использованием цикла событий исключает возможность использования многоядерных процессоров, если я хочу выполнить интенсивную задачу, связанную с ЦП? Я имею в виду, я могу разделить задачу на куски (как объясняется в одной из статей), но это все равно выполняется серийно, а не параллельно. Итак, как механизм JavaScript мог использовать мультикоры для запуска моего кода?
  • Знаете ли вы о других авторитетных источниках, где это поведение для какой-либо конкретной реализации механизма JavaScript формально указано?
  • Каким образом код может быть переносимым между библиотеками и механизмами, если мы не можем принять несколько вещей о средах выполнения?

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

Ответ 1

Существуют определенные предположения/слабые ссылки, которые вы делаете, которые приводят вас к такому выводу. Некоторые из них:

  • ECMAScript ECMA-XXX vs JavaScript vs JavaScriptEngine:

    ECMAscript - это спецификация языка, данная ECMA International. JavaScript является наиболее широко используемым веб-языком, который соответствует ECMAscript. Для большей части ECMAScript и JavaScript являются синонимами (помните, что есть ActionScript). JavaScriptEngine - это реализация (интерпретатор) кода языка JavaScript. Это программа из плоти и костей, работающая с нуля, в отличие от ECMAScript, которая описывает только конечные цели и поведение JavaScript, а JavaScript - код, который использует стандарт ECMAScript. Вы обнаружите, что двигатель сделает больше, чем просто соответствует стандарту ECMAScript. Они находятся в конце спектра спецификации/реализации. Пример этого - ECMA-262/JavaScript/V8.

  • Цикл событий в браузере против цикла событий в node.JS(JSEngine vs JSEnvironment):

    Это выглядит как основной выбор дизайна, сделанный во всех механизмах выполнения JavaScript в браузерах и в node.

    Если вы используете node.JS, возможно, вы использовали основные библиотеки fs/net/http. Они используют излучатели событий, которые подключены к циклу событий, предоставленным libuv. Это расширение для JavaScriptEngine V8, образуя платформу node.JS. Цикл событий здесь включает объекты, такие как потоки, сокеты, файлы или абстрактные запросы. Но события здесь не происходили. Он был впервые использован в браузерах. Браузер реализует DOM, который требует событий для работы с элементами HTML. См. DOM спецификацию и один реализован для Mozilla. Они используют события и требуют цикл событий, созданный поверх JSEngine для использования браузером. Chrome добавляет интерфейс DOM к движку V8, который он вставляет.

    Да, вы почувствуете, что это распространено, из-за необходимого DOM API во всех браузерах. Разработчики Node принесли эту новую обработанную обработку на сервер с помощью libuv, которая обеспечивает неблокирующую асинхронную абстракцию для операций низкого уровня, требуемых на сервере. Как уже указывалось, не все серверные среды используют цикл событий. Возьмите пример Rhino, который буквально использует классы Java для файла, сокеты (все). Если вы действительно используете ядро ​​Java IO, операции с файлом являются синхронными.

Теперь отвечая на ваши вопросы в порядке:

  • объясняется в пункте 2 выше

  • Да, вы можете. Взгляните на Носорога, есть много других. Это возможно в Node, но Node ориентирован на высокопроизводительный веб-сервер и может быть против его zen.

  • Как я уже сказал, цикл событий находится на JSEngine. Это шаблон дизайна, который лучше всего работает с IO. Многопоточная конструкция работает лучше при высоких нагрузках на CPU. Если вы хотите использовать несколько ядер в node.JS, посмотрите cluster модуль. Для браузеров у вас есть веб-мастера.

  • Это зависит от двигателя и двигателя. И как он встроен. Браузеры будут иметь DOM и, следовательно, цикл событий. Серверы могут различаться. Проверьте их характеристики.

  • Для браузера можно сделать его переносным между ними в значительной степени. Нет promises для сервера.

Ответ 2

  • Цикл событий не имеет никакого отношения к самому javascript, это часть среды, а не js engine. Поскольку javascript был разработан в первую очередь для управления пользовательским интерфейсом, он был сильно использован с циклом событий. Но цикл событий является частью реализации пользовательского интерфейса, а не только в javascript, но на любом языке.

  • Да, вы можете. Но это не будет просто двигатель, больше похожий на среду/платформу. Я думаю (но не совсем уверен), что вы можете использовать потоки и связанные с ними вещи в Rhino.

  • Да, да. В node это обычно решается путем создания новых процессов, а в браузере вы можете использовать WebWorkers.

  • Я не могу представить лучшего источника, а затем спецификации. Если что-то не существует, это просто не часть javascript (aka EcmaScript)

Ответ 3

Сегодня я потратил много времени, пытаясь найти ответы на свои вопросы, руководствуясь некоторыми комментариями и другими ответами, оставленными для меня здесь. Я делюсь своими выводами здесь, если другие могут считать их полезными.

Дизайн с событиями в JavaScript для браузеров

Решение о разработке JavaScript таким образом в основном связано с требованиями Архитектуры событий DOM. В этой спецификации мы можем найти явные требования, связанные с реализацией порядка событий и цикла событий. Спецификация HTML5 идет еще дальше и четко определяет термины и задает конкретные требования к реализации цикла событий.

Это, должно быть, привело к разработке движков JavaScript в браузерах. В этой статье Timing and Synchronization in JavaScript, опубликованной Opera, мы можем ясно видеть, что эти требования являются движущей силой дизайна браузера Opera. Также в этой статье из Mozilla, названной Concurrency Model и Event Loop, мы можем найти четкое объяснение тех же концепций дизайна, управляемых событиями как это реализовано Mozilla (хотя документ кажется устаревшим).

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

Обработка ввода пользователя - это самый сложный аспект интерактивных программирование. Приложение может быть чувствительным к нескольким входам устройства, такие как мышь и клавиатура, и могут несколько устройств ввода (например, разных окон). Управление этим отображение многих-ко-многим обычно находится в провинции пользовательского интерфейса Инструменты управления (UIMS). Поскольку большинство UIMS реализовано в последовательных языках они должны прибегать к различным методам эмулировать необходимый concurrency. Как правило, эти инструментальные средства используют event-loop, который контролирует поток входных событий и сопоставляет события с функциями обратного вызова (или обработчиками событий), предоставляемыми прикладного программиста. - Jonh H. Reppy - Параллельное программирование в ML

Использование циклов событий присутствует в других известных инструментах пользовательского интерфейса, таких как Java Swing и Winforms. В Java все работы пользовательского интерфейса должны выполняться в EventDispatchThread whearas в Winforms, все работы UI должны быть выполнены внутри потока, который создал объект Window. Таким образом, даже если эти языки поддерживают истинную многопоточность, они все равно требуют, чтобы весь код пользовательского интерфейса выполнялся в одном потоке выполнения.

Дуглас Крокфорд объясняет историю цикла событий в JavaScript в этом великолепном видео под названием Loopage (стоит посмотреть).

Дизайн с событиями в JavaScript для Node

Теперь решение использовать управляемую событиями конструкцию для Node.js немного менее очевидно. Крокфорд дает хорошее объяснение в видео, распространенном выше. Но также в книге "Прошлое, настоящее и будущее JavaScript" , его автор Axel Rauschmayer говорит:

2009- Node.js, JavaScript на сервере. Node.js позволяет реализовать серверы, которые хорошо работают под нагрузкой. Для этого он использует управляемые событиями неблокирующий ввод-вывод и JavaScript (через V8). Node.js создатель Райан Дал упоминает следующие причины выбора JavaScript:

  • "Потому что он голый и не поставляется с API ввода-вывода". [Node.js может таким образом ввести свои собственные неблокирующие API.]
  • "Веб-разработчики уже используют его". [JavaScript - широко известный язык, особенно в веб-контексте.]
  • "DOM API основан на событиях. Все уже используются для работы без потоков и цикла событий". [Веб-разработчики не боятся Обратные вызовы.]

Итак, похоже, что Ryan Dahl, создатель Node.js, принял во внимание текущий дизайн JavaScript в браузерах, чтобы решить, какой должна быть реализация его неблокирующего, управляемого событиями решения для Node.js.

Последняя версия Node.js похоже, использует библиотеку под названием libuv, предназначенных для реализации таких приложений. Эта библиотека является основной частью дизайна Node. Мы можем найти определение циклов событий в своей документации. Очевидно, это играет важную роль в текущей реализации Node.js.

О других совместимых с EcmaScript двигателями

Спецификация EcmaScript не содержит требований о том, как нужно обрабатывать concurrency в JavaScript. Поэтому это решается путем внедрения языка. Другие модели concurrency могут быть легко использованы, не делая реализацию несовместимой со стандартом.

Лучшие два примера, которые я нашел, это новый Nashorn JavaScript Engine, созданный для Oracle для JDK8, и Rhino JavaScript Engine, созданный Mozilla. Оба они совместимы с EcmaScript, и оба они позволяют создавать классы Java. Ничто в этих двигателях не требует использования управляемых событиями программирования для работы с concurrency. Эти двигатели имеют доступ к библиотеке классов Java и, поскольку они работают поверх JVM, они, вероятно, имеют доступ к другим моделям concurrency, предлагаемым на этой платформе.

Рассмотрим следующий пример из JavaScript, The Definitive Guide, чтобы проиллюстрировать, как использовать Rhino JavaScript.

print(x); // Global print function prints to the console
version(170); // Tell Rhino we want JS 1.7 language features
load(filename,...); // Load and execute one or more files of JavaScript code
readFile(file); // Read a text file and return its contents as a string
readUrl(url); // Read the textual contents of a URL and return as a string
spawn(f); // Run f() or load and execute file f in a new thread
runCommand(cmd, // Run a system command with zero or more command-line args
[args...]);
quit() // Make Rhino exit

Вы можете увидеть, как новый поток может быть создан для запуска файла JavaScript в независимом потоке выполнения.

Об управляемых событиями проектах, многоядерных и True Concurrency

Лучшее объяснение, которое я нашел на эту тему, взято из книги JavaScript Полное руководство. В этой книге Дэвид Фланаган объясняет:

Одной из основных особенностей клиентского JavaScript является то, что он однопоточное: браузер никогда не будет запускать два обработчика событий на в то же время, и он никогда не будет запускать таймер, пока обработчик событий например, работает. Параллельные обновления состояния приложения или документ просто невозможен, и программисты на стороне клиента делают не нужно думать и даже понимать параллельное программирование. Следствием является то, что функции JavaScript на стороне клиента тоже не должны запускаться long: иначе они свяжут цикл событий и веб-браузер станет невосприимчивым к пользовательскому вводу. Именно по этой причине Ajax API всегда асинхронны и причина, что клиентская сторона JavaScript не может иметь простой, синхронный load() или require()функция для загрузки библиотек JavaScript.

Спецификация Web Workers очень тщательно релаксирует однопоточное требование для клиентского JavaScript. Рабочие" он определяет фактически параллельные потоки исполнения. Веб-сотрудники жить в автономной среде исполнения, однако, без доступ к объекту Window или Document и может связываться с основной поток только через асинхронную передачу сообщений. Это значит, что одновременные модификации DOM все еще невозможны, но это также означает, что теперь есть возможность использовать синхронные API и писать долговременные функции, которые не останавливают цикл событий и не браузер. Создание нового рабочего - это не тяжелая операция, как открытие нового окна браузера, но рабочие не являются потоками мухи либо нет, и нет смысла создавать новых рабочих для выполнения тривиальные операции. Сложные веб-приложения могут оказаться полезными для создать десятки рабочих, но маловероятно, что приложение с сотни или тысячи рабочих были бы практичными.

Что о Node.js True Parallelism?

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

В наши дни это выглядит как Node.js кластеризацияявляется решением этой проблемы. Существуют также некоторые библиотеки, такие как Node Worker, которые, похоже, реализуют концепцию Web Worker в Node. Эти библиотеки в основном позволяют создавать новые независимые процессы в Node.js. (Хотя я еще не экспериментировал с этим).

Что такое переносимость?

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

Хотя в сфере браузеров все они работают аналогично, и поскольку Node.js работает в цикле событий, многие вещи могут по-прежнему работать, но не гарантируют, что это должно работать в других системах. Я думаю, это, вероятно, один из недостатков EcmaScript по сравнению с другими более обширными спецификациями, такими как определение виртуальной машины Java или CLR.

Возможно, что-то стандартизируется позже. В будущем EcmaScript сегодня обсуждаются идеи concurrency. См. EcmaSript Wiki: Предложения Strawman Коммуникация Event-Loop concurrency и распространение