Это был субботний вечер, и я проходил курс Udemy по Angular (новый навык, который я пытаюсь приобрести). В нескольких видеороликах я понял, что мои навыки работы с Typescript плохие, и переключился на его документацию. Что ж, в нескольких темах я скептически отнесся к своему пониманию Javascript и начал изучать некоторые основы. И в результате появился этот блог.

Хотя это хорошо известный факт, что «Javascript - однопоточный язык», в этом всегда были некоторые сомнения. Если Javascript однопоточный, как он поддерживает асинхронные вызовы? Почему Node.js, основанный на среде выполнения javascript, не блокирует? Это были некоторые из интригующих вопросов, которые пришли мне в голову. И я решил зафиксировать здесь ход своих мыслей, чтобы соединить различные точки, необходимые для понимания основ Javascript.

Мои вопросы и ответы по Javascript

В: Что означает однопоточный?

A: Будет только один поток (процесс), доступный для выполнения инструкций.

В: Хорошо, Javascript однопоточный?

О: Да, «среда выполнения» Javascript однопоточная. Он выполняет программу javascript. Он поддерживает единственный стек, в который инструкции помещаются для управления порядком выполнения и выталкиваются для выполнения. И пространство кучи, где хранятся ссылки на объекты и собирается мусор.

Давайте посмотрим на эту простую программу и посмотрим, как она выполняется.

Когда скрипт (callstack.js) выполняется, среда выполнения javascript создает глобальный контекст выполнения, помещает его в стек (рис. 1, состояние 1) и выполняет сканирование дальше вниз. Затем инициализация переменной (a = 10) будет помещена в стек. Далее идет вызов функции main (). В отличие от инициализации переменных для вызовов функций, среда выполнения создает новый контекст выполнения функции и помещает его поверх стека вызовов (рис. 1, состояние 2).

Пока элемент управления не достигнет последнего оператора функции main () или оператора return, он продолжает помещать все операторы в стек. В нашем случае console.log () является последним и единственным оператором функции main (), всплывает и выполняется (состояние 3 на рис. 1). Как только все операторы функции будут выполнены, среда выполнения выдает контекст выполнения функции (состояние 3 на рис. 1).

Примечание. Переменная «a» в операторе console.log будет найдена в контексте выполнения функции. Если он недоступен, впоследствии будет произведен поиск в глобальном контексте выполнения.

Среда выполнения будет ждать обработки любых дальнейших операторов, пока не завершится выполнение функции main (). Затем он помещает строку 6 (callstack.js) в стек (состояние 4 на рис. 1). И то же самое будет выскочить и исполнено (Рис. 1, состояние 5). Наконец, как только все операторы скрипта будут выполнены, глобальный контекст выполнения будет выдвинут, оставляя стек свободным для других выполнений.

Обратите внимание: этот пример слишком прост для данного контекста, но выбран намеренно, чтобы не отклоняться от основного обсуждения. Эти две тропы [след 1] [след 2] более справедливы к этой теме, не стесняйтесь сделать объезд для подробного объяснения и вернуться обратно.

В: Хорошо, похоже, это истолковано. Javascript - это интерпретируемый или компилируемый язык?

A: Ну, это зависит от времени выполнения. Javascript по своей природе является интерпретируемым языком программирования. Ему не нужен какой-либо промежуточный компилятор для преобразования его в машиночитаемый код, такой как Java. Хотя это правда, в современных браузерах есть реализация среды выполнения с множеством надстроек для повышения производительности. Одним из основных дополнений является компилятор JIT (Just In Time), который преобразует код javascript в промежуточный машинный язык до того, как браузер его интерпретирует. Вот несколько популярных примеров среды выполнения javascript, которые соответствуют этим стандартам: V8 - Google chrome.

В: Хорошо, вернемся к исходному вопросу. Что ж, если Javascript является однопоточным, как функция setTimeout не блокирует и ее выполнение откладывается, и что может заблокировать / заморозить браузер или стек?

О: Давайте начнем с блокировки с небольшого примера кода.

Output : 
Hit zero at : 5
return

В приведенной выше функции longRunning () цикл while блокирует стек вызовов до тех пор, пока не будет достигнут предел аварийной остановки (условие завершения). Продолжительность периода блокировки может варьироваться от секунд до минут, в зависимости от выбора seed и bailOut. Это типичные последствия неправильной обработки длительных процессов в Javascript.

Давайте внесем некоторые изменения, введя здесь setTimeout ().

Output: 
return
Hit zero at : 5

Обратите внимание, что журнал «return» появляется раньше результата. Вывод доказывает очевидный вопрос, о котором идет речь, что выполнение setTimeout отложено и не блокирует выполнение стека javascript. Рассуждение имеет короткий и более длинный ответ. Короткий ответ: setTimeout - это не функция JavaScript, она предоставляется браузером. Браузер асинхронно управляет запуском и выполнением метода setTimeout.

В: И длинный ответ?

О: Для подробного ответа необходимо введение в некоторые другие функции браузера, которые дополняют возможности кода Javascript.

Они есть,

  • Веб-API
  • Цикл событий

[Фактически, setTimeout - это метод объекта окна. Объект window представляет окно / вкладку браузера.]

Веб-API - это набор API, который предоставляет некоторые из хорошо известных возможностей, таких как AJAX, и некоторые специальные возможности для кода Javascript, такие как кеширование, обработка аудиофайлов, геолокация, уведомления и т. д. И здесь - это полный список API, предоставляемых Chrome.

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

  • Таблица событий - поддерживает триггеры асинхронных вызовов по ссылке для методов обратного вызова.
  • Очередь событий - это место для временного хранения ссылок на метод обратного вызова, прежде чем он может быть помещен в стек выполнения javascript.

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

Как только триггер срабатывает, он перемещает обратный вызов, прикрепленный к асинхронному методу (функция, выполняющая цикл while в нашем случае), в «Очередь событий» (рис. 2. State2). Очередь событий управляет структурой данных очереди и доставляет добавленные в нее сообщения в режиме FIFO (первым пришел - первым ушел).

Наконец, здесь идет наш цикл событий, который представляет собой постоянно работающий процесс, который отслеживает, пуст ли стек вызовов. Когда он обнаруживает, что стек вызовов пуст, он берет первое сообщение из очереди и помещает его в стек (фиг. 2, состояние 3). Вот, если хотите, отличная статья о цикле событий.

В: Хммм. Итак, как все это взаимосвязано?

A: Хорошо, давайте попробуем ответить на наш главный вопрос. Таким образом, очевидно, что, хотя среда выполнения Javascript сама по себе не является асинхронной, она является блокирующей, а не параллельной. Браузер, который управляет средой выполнения Javascript, предоставляет все дополнительные возможности коду Javascript (асинхронный, неблокирующий и параллельный) через веб-API. Где наши исключительные цикл событий, таблица событий и очередь управляют состоянием веб-API и помещают обратные вызовы в стек в нужное время. как объяснено выше.

Вывод

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



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

Ресурсы: