Подтвердить что ты не робот

Как асинхронный javascript интерпретируется и выполняется в Node.js?

В последнее время я много изучал ядро Node.js, и у меня есть несколько вопросов о внутренней работе платформы Node. Насколько я понимаю, Node.js работает так:

enter image description here

У Node есть API, написанный на Javascript, который позволяет программисту взаимодействовать с такими вещами, как файловая система и сеть. Однако все эти функциональные возможности на самом деле выполняются кодом C/C++, также являющимся частью Node. Здесь вещи становятся немного размытыми. Так что работа движка Chrome V8 заключается в том, чтобы "скомпилировать" (интерпретировать?) Javascript в машинный код. V8 написан на C++, а сам язык Javascript определяется ECMA, поэтому такие вещи, как ключевые слова и особенности языка, определяются ими. Это приводит меня к моим первым нескольким вопросам:

  1. Как стандартная библиотека узлов может взаимодействовать с привязками узлов, если привязки узлов написаны в C++?

  2. Как движок Chrome V8 интерпретирует Javascript в контексте Node? Я знаю, что он использует технику, названную JIT, которая была упомянута в аналогичном вопросе: (https://softwareengineering.stackexchange.com/info/291230/how-does-chrome-v8-work-and-why-was-javascript- not-jit-compiled-in-the-first-pl) Но это не объясняет, как Javascript интерпретируется в контексте Node. Является ли движок Chrome V8, который поставляется с Node, точно таким же движком, который работает в браузере Chrome, или он был модифицирован для работы с Node?

Это подводит меня к следующему вопросу. Таким образом, Node имеет управляемый событиями неблокирующий ввод-вывод. Это достигается с помощью цикла событий, который, хотя его часто называют "циклом событий узла", фактически является частью библиотеки libuv, библиотеки C++, предназначенной для обеспечения асинхронного ввода-вывода. На высоком уровне доступ к циклу событий осуществляется через Callbacks, который является нативной функцией Javascript, и одна из причин, по которой Javascript был выбран в качестве языка для проекта Node. Ниже приведена иллюстрация того, как работает цикл обработки событий:

enter image description here

Это также можно продемонстрировать вживую на этом изящном маленьком сайте: http://latentflip.com/loupe/ Допустим, нашему приложению Node необходимо вызвать внешний API. Итак, мы пишем это:

request(..., function eyeOfTheTiger() {
   console.log("Rising up to the challenge of our rival");
});

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

  1. Если Javascript интерпретируется движком Chrome V8, что "контролирует" вставку кода в очередь обратного вызова? Как код JavaScript обрабатывается в цикле событий libuv?

Я нашел это изображение как демонстрацию процесса:

enter image description here

Здесь я не уверен, как именно взаимодействуют движок Chrome V8 и libuv. Я склонен полагать, что привязки узлов облегчают это взаимодействие, но я не совсем уверен, как. На изображении выше кажется, что привязки NodeJS взаимодействуют только с машинным кодом, который был скомпилирован из Javascript V8. Если так, то меня смущает то, как механизм V8 интерпретирует Javascript таким образом, что привязки узлов могут различать обратный вызов и фактический код для немедленного выполнения.

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

Обновление статуса: только что посмотрел фантастический доклад на конференции Sencha (ссылка здесь). Итак, в этом выступлении докладчик упоминает руководство по встраиванию V8 (ссылка здесь) и рассказывает о том, как функции C++ могут быть доступны для Javascript и наоборот. По сути, это работает так, что функции C++ могут быть доступны для V8, а также указывают, как они хотят, чтобы эти объекты были представлены Javascript, и интерпретатор V8 сможет распознавать встроенные функции C++ и выполнять их, если найдет Javascript, который соответствует тому, что вы указали. Например, вы можете представить переменные и функции V8, которые на самом деле написаны в C++. По сути, это то, что делает Node.js; он может добавлять функции, такие как require в Javascript, которые фактически выполняют код C++ при их вызове. Это немного проясняет вопрос № 1, но не совсем показывает, как работает стандартная библиотека Node в сочетании с V8. Также все еще неясно, как libuv взаимодействует с любым из этого.

4b9b3361

Ответ 1

В основном вы ищете Шаблоны V8. Он предоставляет весь ваш код на С++ как функции JavaScript, которые вы можете вызвать из виртуальной машины V8. Вы можете связывать обратные вызовы С++ при вызове функций или при доступе к конкретным свойствам объекта (прочитайте Accessors и Interceptors).

Я нашел очень хорошую статью, которая объясняет все это - Как работает NodeJS?. Это также объясняет, как libuv работает вместе с Node для достижения асинхронности.

Надеюсь, это поможет!