Тщательно разберитесь в цикле событий браузера.

внешний интерфейс JavaScript

Зачем писать этот пост в блоге?

Некоторое время назад я пообщался с друзьями Тутиао и спросил, какие вопросы будут заданы в начале интервью Тутиао, и он сказал, что если бы он брал интервью, то event-loop обязательно задал бы. В тот день мы много разговаривали, и event-loop произвел на меня глубокое впечатление. Причина очень проста, потому что у меня никогда раньше не было глубокого понимания этого.Если бы я столкнулся с этим вопросом во время интервью, считается, что ответ определенно не будет удовлетворительным.

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

Другие статьи можно нажать: GitHub.com/Иветт Л.А. Ю/Б…

1. Предварительные знания

Механизм работы JavaScript:

(1) Все задачи синхронизации выполняются в основном потоке, образуя стек контекста выполнения.

(2) В дополнение к основному потоку существует еще «очередь задач» (task queue). Как только асинхронная задача имеет запущенный результат, событие помещается в «очередь задач».

(3) Как только все задачи синхронизации в «стеке выполнения» будут выполнены, система прочитает «очередь задач», чтобы увидеть, какие события в ней находятся. Затем соответствующие асинхронные задачи завершают состояние ожидания, входят в стек выполнения и начинают выполнение.

(4) Основной поток продолжает повторять третий шаг выше

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

В цикле событий есть одна или несколько очередей задач

В JavaScript есть два вида асинхронных задач:

  1. Задачи макроса: сценарий (общий код), setTimeout, setInterval, setImmediate, ввод-вывод, рендеринг пользовательского интерфейса

  2. Микрозадачи: process.nextTick (Nodejs), Promises, Object.observe, MutationObserver;

2. Что такое цикл событий?

Основной поток считывает события выполнения из «очереди задач».Этот процесс является циклическим.Этот механизм называется циклом событий. Этот механизм заключается в следующем: основной поток будет непрерывно брать задачи из очереди задач и последовательно выполнять их, а после выполнения каждой задачи проверять, не пуста ли очередь микрозадач (специфическим признаком выполнения задачи является то, что функция стек выполнения пуст), если нет Если он пуст, то все микрозадачи будут выполняться одновременно. Затем введите следующий цикл, чтобы взять следующую задачу из очереди задач для выполнения.

Подробное описание:

  1. Выберите текущую очередь задач макроса для выполнения и выберите задачу макроса для входа в очередь задач первой.Если задачи макроса для выбора нет, она перейдет к шагу выполнения микрозадачи.
  2. Устанавливает текущую задачу макроса цикла событий на выбранную задачу макроса.
  3. Запустите задачу макроса.
  4. Установите для текущей задачи цикла событий значение null.
  5. Удалить завершенную задачу макроса из очереди задач макроса.
  6. шаг микрозадач: введите контрольную точку микрозадачи.
  7. Обновление рендеринга пользовательского интерфейса.
  8. Вернитесь к первому шагу.

Конкретные шаги для выполнения проверки микрозадачи следующие:

  1. Устанавливает флаг входа в контрольные точки микрозадачи в значение true.
  2. Когда очередь микрозадач цикла событий не пуста: выберите микрозадачу, которая входит в очередь микрозадач первой; установите текущую выполняющуюся задачу цикла событий на выбранную микрозадачу; запустите микрозадачу; установите текущую выполняющуюся задачу цикла событий на null; будет выполняться Готовая микрозадача удаляется из очереди микрозадач.
  3. Для каждого объекта настроек среды соответствующего цикла событий уведомите их, какие промисы отклонены.
  4. Очистить транзакции для indexedDB.
  5. Установите флаг входа в контрольные точки микрозадачи на false.

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

Значок:

event-loop2

3. Как работает цикл событий?

Начнем с простого примера:

setTimeout(()=>{
    console.log("setTimeout1");
    Promise.resolve().then(data => {
        console.log(222);
    });
});
setTimeout(()=>{
    console.log("setTimeout2");
});
Promise.resolve().then(data=>{
    console.log(111);
});

Задумайтесь, каков результат операции?

Результат бега таков:

111
setTimeout1
222
setTimeout2

Давайте посмотрим, почему?

Давайте подробно объясним, как двигатель JS выполняет этот код:

  1. Нет кода для выполнения в основном потоке
  2. Затем столкнитесь с setTimeout 0, его роль заключается в том, чтобы поместить функцию обратного вызова в очередь задач макроса через 0 мс (эта задача выполняется в следующем цикле событий).
  3. Затем столкнитесь с setTimeout 0, его роль заключается в том, чтобы поместить функцию обратного вызова в очередь задач макроса через 0 мс (эта задача выполняется в следующем цикле событий).
  4. Сначала проверьте очередь микрозадач, то есть очередь микрозадач, и обнаружите, что очередь не пуста, выполните затем обратный вызов первого промиса и выведите '111'.
  5. В это время очередь микрозадач пуста, войдите в следующий цикл событий, проверьте очередь задач макроса, обнаружите, что есть функция обратного вызова setTimeout, немедленно выполните функцию обратного вызова для вывода «setTimeout1», проверьте очередь микрозадач, найдите, что очередь не пуста, выполните затем обратный вызов обещания, вывод '222', очередь микрозадач пуста, и войдите в следующий цикл обработки событий.
  6. Проверьте очередь задач макроса и обнаружите, что есть функция обратного вызова для setTimeout, и немедленно выполните функцию обратного вызова, чтобы вывести «setTimeout2».

Рассмотрим еще раз порядок выполнения следующего кода:

console.log('script start');

setTimeout(function () {
    console.log('setTimeout---0');
}, 0);

setTimeout(function () {
    console.log('setTimeout---200');
    setTimeout(function () {
        console.log('inner-setTimeout---0');
    });
    Promise.resolve().then(function () {
        console.log('promise5');
    });
}, 200);

Promise.resolve().then(function () {
    console.log('promise1');
}).then(function () {
    console.log('promise2');
});
Promise.resolve().then(function () {
    console.log('promise3');
});
console.log('script end');

Задумайтесь, каков результат операции?

Результат бега таков:

script start
script end
promise1
promise3
promise2
setTimeout---0
setTimeout---200
promise5
inner-setTimeout---0

Так почему?

Давайте подробно объясним, как движок JS выполняет этот код:

  1. Во-первых, задачи синхронизации на основном процессе выполняются последовательно, а console.log первого предложения и последнего предложения
  2. Затем столкнитесь с setTimeout 0, его роль заключается в том, чтобы поместить функцию обратного вызова в очередь задач макроса через 0 мс (эта задача выполняется в следующем цикле событий).
  3. Затем столкнитесь с setTimeout 200, его роль состоит в том, чтобы поместить функцию обратного вызова в очередь задач макроса через 200 мс (эта задача выполняется в следующем цикле событий).
  4. После выполнения задачи синхронизации сначала проверьте очередь микрозадач, то есть очередь микрозадач, и обнаружите, что очередь не пуста, выполните обратный вызов then первого промиса, выведите 'promise1', а затем выполните обратный вызов then второе обещание, вывод 'promise3' ', так как возвращение .then() первого обещания все еще является обещанием, второе .then() будет помещено в очередь микрозадач для продолжения выполнения и вывода 'promise2';
  5. В это время очередь микрозадач пуста, войдите в следующий цикл событий, проверьте очередь задач макроса, обнаружите, что есть функция обратного вызова setTimeout, немедленно выполните функцию обратного вызова для вывода «setTimeout---0», проверьте микрозадачу очередь, очередь пуста, введите следующий цикл события.
  6. Проверьте очередь задач макроса и обнаружите, что есть функция обратного вызова setTimeout, немедленно выполните функцию обратного вызова и выведите 'setTimeout --- 200'.
  7. Затем, когда встречается setTimeout 0, его функция состоит в том, чтобы поместить функцию обратного вызова в очередь задач макроса через 0 мс, проверить очередь микрозадач, то есть очередь микрозадач, найти, что очередь не пуста, выполнить затем обратный вызов обещания и выведите «promise5».
  8. В это время очередь микрозадач пуста, войдите в следующий цикл событий, проверьте очередь задач макроса, обнаружите, что есть функция обратного вызова setTimeout, немедленно выполните вывод функции обратного вызова и выведите «inner-setTimeout---0» . Выполнение кода заканчивается.

4. Зачем вам цикл событий?

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

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

5. Справочные статьи:

  1. сегмент fault.com/ah/119000001…
  2. сегмент fault.com/ah/119000001…
  3. сегмент fault.com/ah/119000001…
  4. Вууху. Руань Ифэн.com/blog/2014/1…

Наконец, если вы считаете, что эта запись в блоге немного помогла вам или вдохновила вас, пожалуйста, помогите нажать на звездочку~GitHub.com/Иветт Л.А. Ю/Б…

Рекомендуется обратить внимание на мой публичный номер