эй, твой цикл событий

Node.js внешний интерфейс JavaScript V8
эй, твой цикл событий

Цикл событий в браузере

Использованная литература:Модель параллелизма и цикл обработки событий — MDN
Подробное объяснение механизма работы JavaScript: снова поговорим о цикле событий @ Учитель Руан

Все мы знаем, что JavaScript является однопоточным, а это означает, что в каждый момент времени можно выполнять только одну операцию. Это связано с тем, что JavaScript в основном используется для управленияDOMДа, если он станет многопоточным, браузер запутается и не будет знать, кого слушать. Но хотя js и однопоточный, он может полностью имитировать многопоточность, полагаясь наEvent Loop.

Мы все знаем кодовые точки в js同步а также异步,Так называемый异步На самом деле, он не будет блокировать наш основной поток и ждать выполнения кода основного потока перед выполнением.callback setTimeout setInterval Promise ...Все это нам знакомо异步код


Как показано на рисунке, память в js делится на堆内存(heap)а также栈内存(stack), 堆内存То, что существует, это то, что мы декларируемobjectтип данных,栈内存Хранится в基本数据类型так же как函数执行时рабочее пространство. наш同步код помещается в执行栈а как насчет асинхронного кода? Браузер будетdom事件 ajax setTimeoutПодождите, пока асинхронный код будет поставлен в очередь, подождите执行栈Код в очереди будет выполнен только после того, как код в очереди будет выполнен.Это немного похоже на режим публикации-подписки?


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

Согласно тому, что я сказал ранее, setTimeout будет помещен в очередь и не будет выполняться до тех пор, пока не будет выполнен код в стеке выполнения, поэтому он выведет1, 3, 2


но异步Код тоже отличается:

console.log(1)

setTimeout(() => {
    console.log(2)
}, 0)

Promise.resolve().then(() => {
    console.log(3)
})

вывод всегда1, 3, 2, то естьpromiseсуществуетsetTimeoutвыполнял раньше. Это потому что异步任务разделен на微任务(microtask)а также宏任务(task), порядок выполнения执行栈中的代码 => 微任务 => 宏任务.


стек выполнения

  • 执行栈Код всегда выполняется первым

Микрозадачи:promise MutationObserver...

  • когда执行栈После того, как код будет выполнен, он будет выполнен宏任务队列увидеть раньше微任务队列Есть ли какая-то задача, если есть, будет сначала微任务队列Задачи в宏任务队列

Задача макроса (task):setTimeout setInterval setImmediate(IE专用) messageChannel...

  • ждать执行栈а также微任务队列Он будет выполняться после завершения всех исполнений, и после выполнения каждого宏任务После этого я пойду и посмотрю微任务队列Есть ли новые добавленные задачи? Если да, то они будут добавлены первыми微任务Задача в очереди очищается перед переходом к следующей宏任务

setTimeout(() => {
    console.log('timeout1')
    Promise.resolve().then(() => {
        console.log('promise1')
    })
    Promise.resolve().then(() => {
        console.log('promise2')
    })
}, 100)

setTimeout(() => {
    console.log('timeout2')
    Promise.resolve().then(() => {
        console.log('promise3')
    })
}, 200)
  1. первые дваsetTimeoutПоместить в очередь задач макросов
  2. когда первыйsetTimeout1Когда время выполнения истекло, сначала напечатайте timeout1, а затем поместите его в очередь микрозадач.promise1а такжеpromise2
  3. когда первыйsetTimeout1После завершения выполнения он перейдет к очереди микрозадач, чтобы проверить, не пуста ли она.Он обнаружил, что есть дваpromise, поставлю дваpromiseВыполнить по порядку, а затем перейти к следующей задаче макроса
  4. дваpromiseПосле завершения выполнения задач в очереди микрозадач не будет, а следующая задача будет выполняться в задаче макроса.setTimeout2
  5. когдаsetTimeout2При выполнении выводить сначала таймаут2, а потом вставлять в очередь микрозадачи еще одинpromise2
  6. когдаsetTimeout2После того, как выполнение будет завершено, оно отправится в очередь микрозадач, чтобы проверить и обнаружить, что существует promise3, которыйpromise3воплощать в жизнь
  7. будет печатать последовательноtimeout1 promise1 promise2 timeout2 promise3

Цикл событий в узле

Использованная литература:libuv Документация узла

Все мы знаем, что Node.js — это среда выполнения JavaScript, основанная на движке Chrome V8, то есть она позволяет запускать js на стороне сервера. Но цикл событий в Node моделируется с помощью libuv, который назначает разные задачи разным потокам для формирования цикла событий и возвращает результат выполнения задачи в механизм V8 асинхронным образом.


  • Микрозадачи в узле:process.nextTric promise setImmediate...
  • Задача макроса в узле:setTimeout setInterval...


  • таймеры: выполнять обратные вызовы, срок действия которых истекает в setTimeout() и setInterval().
  • Обратные вызовы ввода-вывода: небольшое количество обратных вызовов ввода-вывода в предыдущем раунде цикла будет отложено до этой стадии раунда.
  • бездействие, подготовка: только для внутреннего использования
  • опрос: самый важный этап, выполнить обратный вызов ввода-вывода, проверить, есть ли таймеры с истекшим сроком действия, которые будут заблокированы на этом этапе при соответствующих условиях
  • проверка: выполнить обратный вызов setImmediate
  • обратные вызовы закрытия: выполнить обратный вызов события закрытия, например socket.on("close",func)

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

setTimeout(() => {
    console.log('timeout1')
    Promise.resolve().then(() => {
        console.log('promise1')
    })
    Promise.resolve().then(() => {
        console.log('promise2')
    })
}, 0)

setTimeout(() => {
    console.log('timeout2')
    Promise.resolve().then(() => {
        console.log('promise3')
    })
}, 0)
  1. первые дваsetTimeoutПоместить в очередь задач макросов
  2. когда первыйsetTimeout1Когда время выполнения истекло, сначала напечатайте timeout1, а затем поместите его в очередь микрозадач.promise1а такжеpromise2
  3. когда первыйsetTimeout1После завершения выполнения переходите к следующемуsetTimeout2
  4. когдаsetTimeout2При выполнении выводить сначала таймаут2, а потом вставлять в очередь микрозадачи еще одинpromise2
  5. Текущая очередь макрозадач очищается и переходите к следующему этапу, чтобы проверить, есть ли задачи в очереди микрозадач.
  6. Очистить очередь микрозадач
  7. Выполняется в среде узла, будет печататься последовательноtimeout1 timeout2 promise1 promise2 promise3