Макрозадачи и микрозадачи в JavaScript

JavaScript

Прежде чем представить интерфейсные макрозадачи и микрозадачи, давайте зададим вопрос и посмотрим.

console.log('1')
setTimeout(() => {
  console.log('2')
})
new Promise((resolve, rejects) => {
  console.log('3')
  resolve()
}).then(() => {
  console.log('4')
})
console.log(5)

Вы можете сначала дать свой собственный ответ и проверить результат, чтобы увидеть, соответствует ли он тому, что вы думаете.

1. Основные понятия

Вот некоторые базовые знания JavaScript

  1. Что касается среды выполнения кода, то при выполнении кода JavaScript движок создаст текущий блок кода.среда исполнения, когда дело доходит до использования переменных, можно найти только переменные текущей среды и переменные внешней среды, содержащие текущую среду выполнения.Глобальная среда — это самая внешняя среда выполнения..
  2. JavaScript этоодин поток.
  3. Когда JavaScript обрабатывает асинхронные операции, он используетМеханизм цикла событий.

2. Макрозадача, микрозадача и механизм цикла событий

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

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

Это действительно так?

Конечно нет. Если это так, то нет никакой разницы в смысле макрозадач и микрозадач.

3. Макрозадачи и микрозадачи

Прежде всего, мы должны настаивать на одном моменте: между макрозадачами и микрозадачами должна быть абсолютная разница.

Взгляните на действия, которые могут запускать задачи макросов в среде браузера (в других средах они будут другими):

  1. операции ввода/вывода
  2. setTimeout
  3. setInterval
  4. requestAnimationFrame (спорный, обсуждается позже)

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

Так кто делает эту операцию?является хост-средой. Возьмем браузер в качестве примера, когда JavaScript выполняется для setTimeout, он сообщает браузеру: «Эй, мальчик! Есть таймер, ты можешь посмотреть его для меня и сказать мне, когда время истекло». В это время браузер выполнит операцию синхронизации.После того, как синхронизация завершится, обратный вызов setTimeout будет помещен в очередь обратного вызова цикла событий JavaScript. Таким образом, JavaScript может обрабатывать этот обратный вызов в последующих исполнениях.

Давайте взглянем на перечисленные выше задачи макросов с 4-точечным триггером, все они связаны с браузером!

Итак, мое личное понимание:Задача макроса — это обратный вызов, созданный JavaScript и средой хоста.Все задачи, которые должны быть обработаны средой хоста и которые будут помещены в очередь обратного вызова, — это задачи макроса.

Операции, запускающие микрозадачи в браузере:

  1. Promise
  2. MutationObserver

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

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

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

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

Итак, здесь мы можем точно сказать:Микрозадачи, сгенерированные одной и той же очередью выполнения, всегда выполняются перед макрозадачами..

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

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

4. О запросеAnimationFrame

Сначала я был правОпределение requestAnimationFrame — макрозадача, потому что в тестеЯ использую следующий код, когда requestAnimationFrame

const testElement = document.getElementById('testElement')
setTimeout(() => {
  console.log(performance.now(), 'settimeout')
}, 0)
requestAnimationFrame(() => {
  console.log(performance.now(),
 'requestAnimationFrame')
})
var observer = new MutationObserver(() => {
  console.log('MutationObserver')
});
observer.observe(testElement, {
 childList: true 
})
const div = document.createElement('div')testElement.appendChild(div)
new Promise(resolve => {
  console.log('promise')  resolve()
}).then(() => console.log('then'))
console.log(performance.now(), 'global')

Будут различия в выводе браузера, и после нескольких запусков появятся два результата.

Первый:


Другой:


Сначала я поместил requestAnimationFrame в задачу макроса, потому что он в основном выполняется после выполнения обратного вызова setTimeout. и поставь этоРезультат интерпретируется так, что поскольку браузер выполняет рендеринг, время каждого выполнения будет разным, поэтому результатrequestAnimationFrame иВремя, когда setTimeout помещается в обратный вызов, несовместимо, что приводит к несогласованному времени обратного вызова.

Но такое вынужденное объяснение все же несостоятельно.Хе-хе, как начинающему молодому человеку, решившему стать отличным Программистом, мне еще нужно найти аргументы. _____ южный ветер

Проведя некоторое исследование, я увидел этоСпецификацияпозже нашелВ цикле событий галочка содержит процесс рендеринга браузера,а такжеТриггер requestAnimationFrame срабатывает до перерисовки браузера Документация MDN описывает его следующим образом:

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

так,Время обратного вызова requestAnimationFrame также находится в текущем тике, поэтому это не макрозадача, но и не микрозадача, и она ранжируется после микрозадачи.


Конечно, если есть большой парень, который может просветить меня по этому вопросу, пожалуйста, оставьте сообщение в области комментариев. /ручной спринклер /ручной спринклер

5. Резюме

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

«Любое приложение, которое можно написать на JavaScript, в конечном итоге будет написано на JavaScript» — закон Этвуда.

Дополнительный

  1. Задача макроса в среде Node:
    1. операции ввода/вывода
    2. setTimeout
    3. setInterval
    4. setImmediate
  2. Микрозадачи в среде Node:
    1. Promise
    2. process.nextTick
  3. Добавив цикл while к задержке, вы сможете лучше понять цикл обработки событий:
console.log('1')
setTimeout(() => {
  console.log('2')
})
new Promise((resolve, rejects) => {
  console.log('3')
  resolve()
}).then(() => {
  let i = 0
  while(i < 1000000000) {
    i++
  }
  console.log('4')
})
let i = 0
while(i < 1000000000) {
  i++
}
console.log(5)