Очередь событий в браузере

внешний интерфейс JavaScript браузер Promise
Очередь событий в браузере

Как мы все знаем, JavaScript работает в одном потоке и может выполняться асинхронно.Вообще говоря, такого рода язык, который является одновременно однопоточным и асинхронным, управляется событиями.Бывает, что браузер предоставляет такую ​​среду для JavaScript.

  1. setTimeout(function(argument) {

  2.  console.log('---1---')

  3. }, 0)

  4. console.time("test")

  5. for (var i = 0; i < 1000000; i++) {

  6.  i === (100000 - 1)

  7. }

  8. console.timeEnd("test")

  9. console.log('---2---')

скопировать код

Вывод на моем компьютере:

test: 5.4892578125ms

---2---

---1---

Эй, это не имеет смысла, очевидно, я настроил его на печать '---1---' через 0 миллисекунд, откройте интерфейсную Библию и посмотрите, в ней есть предложение:

This is because even though setTimeout was called with a delay of zero, it's placed on a queue and scheduled to run at the next opportunity; not immediately. Currently-executing code must complete before functions on the queue are executed, thus the resulting execution order may not be as expected.

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

Это включает в себя несколько потоков браузера. Вообще говоря, браузер будет иметь следующие потоки

  1. поток движка js (интерпретирует и выполняет код js, пользовательский ввод, сетевые запросы)

  2. Поток графического интерфейса (отрисовка пользовательского интерфейса, взаимоисключающий основной поток js)

  3. поток сетевых запросов http (обработка пользовательских запросов на получение, публикацию и другие запросы и отправка функции обратного вызова в очередь задач после возврата результата)

  4. Поток триггера синхронизации (вставьте функцию выполнения в очередь задач после истечения времени ожидания setTimeout, setInterval)

  5. Поток обработки событий браузера (помещайте щелчок, мышь и другие интерактивные события в очередь событий после их возникновения)

Стек выполнения функций JavaScript и обратный вызов

  1. function test1() {

  2.  test2()

  3.  console.log('大家好,我是test1')

  4. }

  5. function test2() {

  6.  console.log('大家好,我是test2')

  7. }

  8. function main() {

  9.  console.log('大家好,我是main')

  10.  setTimeout(() => {

  11.    console.log('大家好,我是setTimeout')

  12.  }, 0)

  13.  test1()

  14. }

  15. main()

скопировать код

Результат выполнения следующий:

Привет, я главный

Всем привет, я test2

Всем привет, я test1

Привет всем, меня зовут setTimeout

Когда мы вызываем функцию, ее адрес, параметры и локальные переменные помещаются в стек.

step1: Сначала вызывается функция main(), она входит в стек выполнения и печатает «Всем привет, я главный».

Шаг 2: Когда встречается setTimeout, поместите функцию обратного вызова в очередь задач.

step3: main вызывает test1, и функция test1 входит в стек и будет выполняться

шаг 4: выполняется test1, test1 вызывает test2

шаг 5: выполнить test2, вывести «Привет всем, я test2»

шаг 6: после выполнения test2 он выскакивает из стека и возвращается к test1 и печатает «Привет всем, я test1».

Шаг 6: После выполнения основного потока войдите в очередь обратного вызова, чтобы выполнить функцию обратного вызова setTimeout и напечатать «Привет всем, я setTimeout». На этом этапе выполняется вся программа, но цикл событий ожидает других функции обратного вызова.

В коде наверное так

  1. while (queue.waitForMessage()) {

  2.  queue.processNextMessage()

  3. }

скопировать код

Этот поток будет ожидать других функций обратного вызова, таких как click, setTimeout и т. д.

Используйте диаграмму, чтобы представить следующее:

макрозадача и микрозадача

Далее посмотрите на код и подумайте, каков результат этой операции.

  1.  setTimeout1 = setTimeout(function() {

  2.    console.log('---1---')

  3.  }, 0)

  4.  setTimeout2 = setTimeout(function() {

  5.    Promise.resolve()

  6.      .then(() => {

  7.        console.log('---2---')

  8.      })

  9.    console.log('---3---')

  10.  }, 0)

  11.  new Promise(function(resolve) {

  12.    console.time("Promise")

  13.    for (var i = 0; i < 1000000; i++) {

  14.      i === (1000000 - 1) && resolve()

  15.    }

  16.    console.timeEnd("Promise")

  17.  }).then(function() {

  18.    console.log('---4---')

  19.  });

  20.  console.log('---5---')

скопировать код

Выше мы проанализировали, что браузер будет помещать асинхронную функцию обратного вызова в очередь задач.Согласно этой идее, когда программа работает, она сначала столкнется с функцией setTimeout, а функцию обратного вызова setTimeout поместит в очередь задач. если вы продолжите выполнение, вы столкнетесь с функцией setTimeout, но есть некоторые исключения для функции обратного вызова этого setTimeout.В функции обратного вызова есть объект Promise, но мы не будем ждать, пока наступит очередь очереди задач Затем браузер интерпретатор столкнется с новой операцией объекта Promise, которая не выполняется асинхронно. Сначала он запустит таймер выполнения программы, а затем выведет время, необходимое для увеличения от 0 до 1 000 000. В я = 999999, состояние Promise станет разрешающим, а затем поместит функцию обратного вызова, которая должна быть выполнена по разрешению, в очередь задач и, наконец, выполнит до конца программы, в это время вывод будет '---5--- `.

Согласно приведенному выше анализу, порядок вывода нашей программы:

Время, необходимое программе для увеличения от 0 до 1000000

---5---

---1---

---3---

---4---

---2---

Используйте диаграмму, чтобы представить следующее:

Затем выполните его в браузере и посмотрите результаты.Результаты следующие:

Promise: 5.151123046875ms

---5---

---4---

---1---

---3---

---2---

why

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

microtasks:

  • process.nextTick

  • promise

  • Object.observe

  • MutationObserver

macrotasks:

  • setTimeout

  • setInterval

  • setImmediate

  • I/O

  • Рендеринг пользовательского интерфейса

Согласно спецификации whatwg:

  • Цикл событий (цикл событий) будет иметь одну или несколько очередей задач (очередь задач)

  • Каждый цикл событий имеет очередь микрозадач.

  • task queue == macrotask queue != microtask queue

  • Задача задачи может быть помещена в очередь макрозадач или в очередь микрозадач.

  • Стек вызовов очищается (остается только глобальный), после чего выполняются все микрозадачи. Когда все исполняемые микрозадачи выполнены. Цикл снова начинается с макрозадачи, обнаруживает, что одна из очередей задач выполняется, затем выполняются все микрозадачи и так далее.

поэтому правильные шаги выполнения должны быть:

Шаг 1: Выполните скрипт, поместите этот скрипт в очередь задач, в это время

  1.  stacks: []

  2.  task queue: [script]

  3.  microtask queue: []

скопировать код

шаг 2: когда встречается setTimeout1, setTimeout можно рассматривать как задачу, поэтому в это время

  1.  stacks: [script]

  2.  task queue: [setTimeout1]

  3.  microtask queue: []

скопировать код

шаг 3: продолжить выполнение, столкнуться с setTimeout2, поместить setTimeout2 в очередь задач

  1.  stacks: [script]

  2.  task queue: [setTimeout1,setTimeout2]

  3.  microtask queue: []

скопировать код

шаг 4: продолжить выполнение, новый промис, перейти вниз в соответствии с процессом синхронизации, вывести время выполнения в новом промисе

  1.  stacks: [script]

  2.  task queue: [setTimeout1,setTimeout2]

  3.  microtask queue: []

скопировать код

Шаг 5: Когда i = 99999, происходит resolve(), и сегмент кода с успешным обратным вызовом помещается в очередь микрозадачи.

  1.  stacks: [script]

  2.  task queue: [setTimeout1,setTimeout2]

  3.  microtask queue: [console.log('---4---')]

скопировать код

шаг 6: продолжить выполнение - это console.log('---5---'), в это время задача в очереди стеков заканчивается

  1.  stacks: []

  2.  task queue: [setTimeout1,setTimeout2]

  3.  microtask queue: [console.log('---4---')]

скопировать код

Шаг 7: Очередь стеков пуста. В это время поток опроса событий поместит задачи из очереди микрозадач в стеки, а затем выполнит вывод '---4---'

  1.  stacks: []

  2.  task queue: [setTimeout1,setTimeout2]

  3.  microtask queue: []

скопировать код

Шаг 8: Очередь стеков снова пуста, и очередь микрозадач также пуста. Затем будут выполняться задачи в очереди задач. Первая — setTimeout1, а вывод — «---1---»

  1.  stacks: [setTimeout1,setTimeout2]

  2.  task queue: []

  3.  microtask queue: []

скопировать код

step8: Затем setTimeout2 встречает в нем Promise.resolve(), помещает успешную функцию обратного вызова в очередь микрозадач, а затем выводит '---3---'

  1.  stacks: [setTimeout2]

  2.  task queue: []

  3.  microtask queue: [Promise.resolve()]

скопировать код

Шаг 9: После завершения выполнения setTimeout2 выполните очередь микрозадач и выведите «---2---», пока выполнение сегмента кода завершено.

  1.  stacks: []

  2.  task queue: []

  3.  microtask queue: []

скопировать код

В простом предложении:Сначала выполняется вся макрозадача кода js, после выполнения кода синхронизации микрозадача выполняет микрозадачу, и никакая микрозадача не выполняет следующую макрозадачу, и так далее и так далее до конца

——————————————————

Нажмите и удерживайте QR-код, следуйте Dazhuanzhuan FE