JS-движок, среда выполнения и обзор стека вызовов

JavaScript

Обзор

Почти все слышали о концепции движка V8, и большинство людей знают, что JavaScript работает в одном потоке или использует очередь обратных вызовов.

Далее мы подробно рассмотрим эти концепции и объясним, как на самом деле работает JavaScript. Когда вы знаете эти детали, вы можете рационально использовать существующий API для написания лучших неблокирующих приложений. Если вы новичок в JavaScript, этот блог поможет вам понять, почему JavaScript такой странный по сравнению с другими языками.

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

JavaScript-движок

Популярным движком JavaScript является движок Google V8. Например, движок V8 используется в Chrome и Node.js. На следующем изображении показан очень простой предварительный просмотр двигателя V8:

Двигатель V8 состоит из двух основных компонентов:

  • Куча памяти - область выделения памяти

  • Stack Call Stack - код стека работает

Время выполнения

Большинство разработчиков JavaScript использовали API-интерфейсы браузера (например, setTimeout). Однако ни один из этих API не предоставляется движком. Так откуда они берутся? Реальность немного сложнее.

Так что есть много чего еще пить кроме двигателя. Существуют веб-API, предоставляемые браузерами, такие как DOM, AJAX, setTimeout и т. д. Тогда есть очень известный цикл событий и очередь вызовов.

стек вызовов

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

Стек вызовов — это структура данных, которая записывает, где выполнялась программа. При вызове функции мы помещаем ее на вершину стека. При возврате из функции мы извлекаем ее из самого верха стека. Это все, что делает стек вызовов.

Давайте посмотрим на пример, посмотрите на следующий код:

function multiply(x, y) {
    return x * y;
}
function printSquare(x) {
    var s = multiply(x, x);
    console.log(s);
}
printSquare(5);

Когда движок начинает выполнять этот код, стек вызовов пуст. Далее каждый шаг выглядит так:

Каждый раз, когда вы входите в стек вызовов, он становится кадром стека. Вот как составляется запись стека при возникновении исключения, в основном состояние стека вызовов при возникновении исключения. Взгляните на следующий код:

function foo() {
    throw new Error('SessionStack will help you resolve crashes :)');
}
function bar() {
    foo();
}
function start() {
    bar();
}
start();

При работе в Chrome (при условии, что этот код находится в файле foo.js) будет сгенерирована следующая запись стека:

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

function foo() {
    foo();
}
foo();

Когда движок начинает выполнять этот код, сначала вызывается функция "foo", но эта функция является рекурсивной, начинает вызывать себя и не имеет конечного условия. Таким образом, на каждом шаге выполнения одна и та же функция снова и снова добавляется в стек вызовов, выглядя так:

Однако в какой-то момент количество вызовов функций в стеке вызовов превысит фактический размер стека вызовов, и в этот момент браузер решит принять меры и выдаст ошибку, и мы увидим подсказку, подобную следующей:

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

Concurrency & the Event Loop

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

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

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

Это не лучший пользовательский опыт, не так ли?

Итак, как мы можем запускать тяжелый код, не блокируя пользовательский интерфейс, чтобы браузеру не нужно было ждать ответа? Решение — асинхронные обратные вызовы.

Мы подробно опишем в следующем разделе.

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

Эта статья переведена с:blog.session stack.com/how-does-hurry up…