Event Loop

внешний интерфейс JavaScript Chrome

Обзор

Думаю, многие из вас видели выступление Филипа Робертса о цикле событий на JS-Conf.

Эта статья предназначена для записи запоздалой мысли.

Event Loop

период, термин

JSModel

Call Stack

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

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

Посмотрите на следующий код: Мы понимаем процесс выполнения стека вызовов.


function foo (b) {
  var a = 5
  return a * b + 10
}

function bar (x) {
  var y = 3
  return foo(x * y)
}

console.log(bar(6)) // 100

Satck

// 执行过程如下
1. 首先找到即将开始执行的main函数
2. 从console.log(bar(6)) 开始执行代码, 它被推到调用栈的底部.
3. 然后bar()被推到console.log(bar(6))的顶部
4. 之后foo()被推到bar()的顶部, 但是当它执行后立即返回, 被推出堆栈.
5. 接着将bar()弹出
6. 最后在console.log()被弹出,把返回值100打印在输出台.


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

Error Stack

Иногда мы вызываем функцию рекурсивно много раз, она входит в бесконечный цикл.Для браузера Chrome размер стека ограничен (16000 кадров), поэтому он выдает ошибку Max Stack.

Max Stack

Heap

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

Ссылочный тип, размер значения не фиксирован, а адрес хранения в памяти стека указывает на объект в памяти кучи. доступ осуществляется по ссылке. Как показано на рисунке ниже: Память стека хранит только адрес доступа к объекту и выделяет место для этого значения в памяти кучи. Поскольку размер таких значений не фиксирован, их нельзя хранить в памяти стека. Но размер адреса памяти фиксирован, поэтому адрес памяти можно хранить в памяти стека. Таким образом, при запросе переменной ссылочного типа сначала считывается адрес памяти из стека, а затем по адресу находится значение в куче. Для этого мы называем это доступом по ссылке.

Heap and Stack

Quene

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

Quene — это структура данных «первым поступил — первым обслужен», и события впереди считываются главным потоком в первую очередь. Процесс чтения основного потока в основном автоматический.Как только стек выполнения (Call Stack) опустеет, первое событие Quene автоматически попадет в основной поток. Однако из-за наличия функции «таймер» основной поток должен сначала проверить время выполнения, а некоторые события могут войти в очередь событий только по истечении указанного времени, прочитать событие и выполнить соответствующий обратный вызов события.

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

Я думаю ключ здесь в том, чтобы понять, какой код будет входить в Quene, лично мне нравится называть его "асинхронным кодом". Мы все знаем, что JS - это однопоточный голос, если оба используют синхронный и не поддерживают асинхронный, можно представить стек при загрузке ресурсов Как будет выглядеть блокировка Так JS поддерживает асинхронные обратные вызовы Некоторые люди могут спросить, почему асинхронный код не может напрямую войти в стек вызовов На самом деле, поскольку это асинхронный код, очевидно неразумно, что порядок входа в стек вызовов вставляется случайным образом.

// 到了时间才会推入到队列

console.log(1)
setTimeout(() => {
  console.log(2)
}, 200)
setTimeout(() => {
  console.log(3)
}, 0)
console.log(4)

// 1 4 3 2 

Асинхронные события на стороне браузера: события DOM, http-запросы, setTimeout и другие асинхронные события.

Event Loop

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

Event Loop

Не по теме

Про очередь: На самом деле она делится на макрозадачи/микрозадачи.

// 测试代码

function execOrder () {
  setTimeout(() => console.log('timeout'), 0)
  let promise = new Promise((resolve, reject) => {
	console.log('Promise')
	resolve()
  })
  promise
  	.then(() => {
  		console.log('resolved')
  	})
  console.log('hi')
}

// 执行顺序: Promise hi resolved timeout