Думаю из содержания выводимых сообщений опытные разработчики уже поняли в чём дело :)
Перед тем как дальше читать важно знать:
Микрозадачи выполняются после выхода из создавшей их функции или программы и только в том случае, если стэк выполнения JavaScript пуст, но перед возвратом управления циклу событий, используемому пользовательским агентом для управления средой выполнения сценария
Например промис-ы используют очередь микрозадач для выполнения своих колбэк-функций
Новая задача кладётся в стэк выполнения, только после того как отрабатывают все микрозадачи предыдущей задачи. Потому если какая-та задача или микрозадача генерирует бесконечное количество микрозадач (например с помощью queueMicrotask), то следующая задача не выполнится никогда
Про объявление переменных рассказывать не буду, т.к. к сути вопроса не имеет отношения. Также не буду рассказывать про слушателя кнопки button2
т.к. он просто нужен чтобы в желаемое время вызвать button1.click()
и console.clear()
т.к. он просто нужен чтобы не засорять консоль
Общее для обоих методов:
button1.addEventListener(/*1*/)
- добавляется первый слушатель на кнопку button1
button1.addEventListener(/*2*/)
- добавляется второй слушатель на кнопку button1
Клик на кнопку физически
Вызывается первый слушатель:
1.1. Вызывается первая колбэк-функция - в стэк выполнения ставится первая колбэк-функция
1.2. Promise.resolve().then(() => console.log('Microtask 1'))
- в очередь микрозадач ставится новая микрозадача console.log('Microtask 1')
1.3. console.log('Task 1')
- в стэк выполнения ставится новая задача
1.4. Т.к. в первой колбэк-функции больше ничего нет, то в стэке выполняется, то что там лежит в обратном порядке. Последнее что мы туда положили - console.log('Task 1')
, а значит он и выполнится и мы в консоли увидим Task 1
. После выполнения она убирается из стэка выполнения
1.5. Теперь в стэке осталась первая колбэк-функция, но т.к. ей больше нечего делать, то она заканчивает свою работу и убирается из стэка выполнения
1.6. Наконец стэк выполнения становится пустым и начинают выполняться микрозадачи. Там одна микрозадача - console.log('Microtask 1')
, после его выполнения мы увидим Microtask 1
Т.к. теперь очередь микрозадач пустой, то вызывается второй слушатель:
2.1. Вызывается вторая колбэк-функция - в стэк выполнения ставится вторая колбэк-функция
2.2. Promise.resolve().then(() => console.log('Microtask 2'))
- в очередь микрозадач ставится новая микрозадача console.log('Microtask 2')
2.3. console.log('Task 2')
- в стэк выполнения ставится новая задача
2.4. Т.к. в первой колбэк-функции больше ничего нет, то в стэке выполняется, то что там лежит в обратном порядке. Последнее что мы туда положили - console.log('Task 2')
, а значит он и выполнится и мы в консоли увидим Task 2
. После выполнения она убирается из стэка выполнения
2.5. Теперь в стэке осталась первая колбэк-функция, но т.к. ей больше нечего делать, то она заканчивает свою работу и убирается из стэка выполнения
2.6. Наконец стэк выполнения становится пустым и начинают выполняться микрозадачи. Там одна микрозадача - console.log('Microtask 2')
, после его выполнения мы увидим Microtask 2
Теперь мы явно увидели почему порядок вывода при клике физически таков Task 1, Microtask 1, Task 2, Microtask 2
Клик на кнопку из кода
Вызывается button1.click()
- в стэк выполнения ставится button1.click()
Вызывается первый слушатель:
2.1. Вызывается первая колбэк-функция - в стэк выполнения ставится первая колбэк-функция
2.2. Promise.resolve().then(() => console.log('Microtask 1'))
- в очередь микрозадач ставится новая микрозадача console.log('Microtask 1')
2.3. console.log('Task 1')
- в стэк выполнения ставится новая задача
2.4. Т.к. в первой колбэк-функции больше ничего нет, то в стэке выполняется, то что там лежит в обратном порядке. Последнее что мы туда положили - console.log('Task 1')
, а значит он и выполнится и мы в консоли увидим Task 1
. После выполнения она убирается из стэка выполнения
2.5. Далее на очереди первая колбэк-функция, но т.к. ей больше нечего делать, то она заканчивает свою работу и убирается из стэка выполнения
2.6. И вот ключевой момент - наш стэк выполнения не пуст, там всё ещё осталась невыполненная функция button1.click()
. Когда очередь доходит до него, то он продолжает свою работу
Вызывается второй слушатель:
2.1. Вызывается вторая колбэк-функция - в стэк выполнения ставится вторая колбэк-функция
2.2. Promise.resolve().then(() => console.log('Microtask 2'))
- в очередь микрозадач ставится новая микрозадача console.log('Microtask 2')
. Теперь у нас в очереди микрозадач 2 микрозадачи - console.log('Microtask 1')
и console.log('Microtask 2')
2.3. console.log('Task 2')
- в стэк выполнения ставится новая задача
2.4. Т.к. в первой колбэк-функции больше ничего нет, то в стэке выполняется, то что там лежит в обратном порядке. Последнее что мы туда положили - console.log('Task 2')
, а значит он и выполнится и мы в консоли увидим Task 2
. После выполнения она убирается из стэка выполнения
2.5. Теперь в стэке осталась первая колбэк-функция, но т.к. ей больше нечего делать, то она заканчивает свою работу и убирается из стэка выполнения
2.6. Очередь доходит до button1.click()
и вот теперь то он и завершает свою работу, вызвав всех слушателей и убирается из стэка выполнения
2.7. Наконец стэк выполнения становится пустым и начинают выполняться микрозадачи. Т.к. это очередь, а не стэк, то выполняются микрозадачи по очереди. Первым на очереди у нас console.log('Microtask 1')
, а значит мы увидим Microtask 1
2.8. Вторым на очереди у нас console.log('Microtask 2')
, а значит мы увидим Microtask 2
Теперь мы явно увидели почему порядок вывода при клике на кнопку из кода таков Task 1, Task 2, Microtask 1, Microtask 2