Прежде чем представить интерфейсные макрозадачи и микрозадачи, давайте зададим вопрос и посмотрим.
console.log('1')
setTimeout(() => {
console.log('2')
})
new Promise((resolve, rejects) => {
console.log('3')
resolve()
}).then(() => {
console.log('4')
})
console.log(5)
Вы можете сначала дать свой собственный ответ и проверить результат, чтобы увидеть, соответствует ли он тому, что вы думаете.
1. Основные понятия
Вот некоторые базовые знания JavaScript
- Что касается среды выполнения кода, то при выполнении кода JavaScript движок создаст текущий блок кода.среда исполнения, когда дело доходит до использования переменных, можно найти только переменные текущей среды и переменные внешней среды, содержащие текущую среду выполнения.Глобальная среда — это самая внешняя среда выполнения..
- JavaScript этоодин поток.
- Когда JavaScript обрабатывает асинхронные операции, он используетМеханизм цикла событий.
2. Макрозадача, микрозадача и механизм цикла событий
Учащиеся, знакомые с циклом событий, знают, что в цикле событий асинхронные события не помещаются в текущую очередь выполнения задачи, а приостанавливаются и помещаются в другую очередь обратного вызова. После того, как выполнение текущей очереди задач завершено, движок JavaScript возвращается, чтобы проверить, есть ли задачи, ожидающие выполнения в очереди обратного вызова, Если есть, он добавит первую задачу в очередь выполнения, а затем повторит это непрерывно обрабатывать.
С точки зрения явления, асинхронные операции, генерируемые макрозадачами и микрозадачами, будут выполняться после завершения очереди выполнения, поэтому кажется, что и макрозадачи, и микрозадачи помещаются в очередь обратного вызова.
Это действительно так?
Конечно нет. Если это так, то нет никакой разницы в смысле макрозадач и микрозадач.
3. Макрозадачи и микрозадачи
Прежде всего, мы должны настаивать на одном моменте: между макрозадачами и микрозадачами должна быть абсолютная разница.
Взгляните на действия, которые могут запускать задачи макросов в среде браузера (в других средах они будут другими):
- операции ввода/вывода
- setTimeout
- setInterval
- requestAnimationFrame (спорный, обсуждается позже)
Возьмем, к примеру, setTimeout. Поскольку JavaScript представляет собой один поток, операция синхронизации setTimeout не должна выполняться JavaScript, иначе выполнение кода будет заблокировано.
Так кто делает эту операцию?является хост-средой. Возьмем браузер в качестве примера, когда JavaScript выполняется для setTimeout, он сообщает браузеру: «Эй, мальчик! Есть таймер, ты можешь посмотреть его для меня и сказать мне, когда время истекло». В это время браузер выполнит операцию синхронизации.После того, как синхронизация завершится, обратный вызов setTimeout будет помещен в очередь обратного вызова цикла событий JavaScript. Таким образом, JavaScript может обрабатывать этот обратный вызов в последующих исполнениях.
Давайте взглянем на перечисленные выше задачи макросов с 4-точечным триггером, все они связаны с браузером!
Итак, мое личное понимание:Задача макроса — это обратный вызов, созданный JavaScript и средой хоста.Все задачи, которые должны быть обработаны средой хоста и которые будут помещены в очередь обратного вызова, — это задачи макроса.
Операции, запускающие микрозадачи в браузере:
- Promise
- 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» — закон Этвуда.
Дополнительный
- Задача макроса в среде Node:
- операции ввода/вывода
- setTimeout
- setInterval
- setImmediate
- Микрозадачи в среде Node:
- Promise
- process.nextTick
- Добавив цикл 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)