Обзор
Думаю, многие из вас видели выступление Филипа Робертса о цикле событий на JS-Conf.
Эта статья предназначена для записи запоздалой мысли.
период, термин
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
// 执行过程如下
1. 首先找到即将开始执行的main函数
2. 从console.log(bar(6)) 开始执行代码, 它被推到调用栈的底部.
3. 然后bar()被推到console.log(bar(6))的顶部
4. 之后foo()被推到bar()的顶部, 但是当它执行后立即返回, 被推出堆栈.
5. 接着将bar()弹出
6. 最后在console.log()被弹出,把返回值100打印在输出台.
В качестве примера более наглядным может быть следующее изображение стека ошибок:
Иногда мы вызываем функцию рекурсивно много раз, она входит в бесконечный цикл.Для браузера Chrome размер стека ограничен (16000 кадров), поэтому он выдает ошибку Max Stack.
Heap
Куча: объекты размещаются в куче.Когда речь идет о памяти кучи, вам необходимо понимать память стека, ссылочные типы и базовые типы.
Ссылочный тип, размер значения не фиксирован, а адрес хранения в памяти стека указывает на объект в памяти кучи. доступ осуществляется по ссылке. Как показано на рисунке ниже: Память стека хранит только адрес доступа к объекту и выделяет место для этого значения в памяти кучи. Поскольку размер таких значений не фиксирован, их нельзя хранить в памяти стека. Но размер адреса памяти фиксирован, поэтому адрес памяти можно хранить в памяти стека. Таким образом, при запросе переменной ссылочного типа сначала считывается адрес памяти из стека, а затем по адресу находится значение в куче. Для этого мы называем это доступом по ссылке.
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
Основная работа цикла обработки событий состоит в том, чтобы просмотреть стек и очередь задач, и, когда очередь увидит, что стек пуст, поместить в стек первое событие в очереди. Обратный вызов для текущего события обрабатывается до обработки любых других сообщений.
Не по теме
Про очередь: На самом деле она делится на макрозадачи/микрозадачи.
// 测试代码
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