Глубокое понимание EventLoop в JavaScript

внешний интерфейс JavaScript
Глубокое понимание EventLoop в JavaScript

Мало знаний, большой вызов! Эта статья участвует в "Необходимые знания для программистов«Творческая деятельность

Эта статья участвовала в "Проект «Звезда раскопок»”, чтобы выиграть творческий подарочный пакет и бросить вызов творческим поощрительным деньгам.

предисловие

Event Loopто есть цикл событий, это браузер илиNodeМеханизм, решающий проблему отсутствия блокировки при работе в одном потоке.

в формальном исследованииEvent LoopПеред этим необходимо решить несколько вопросов:

  1. Что такое синхронный и асинхронный?
  2. JavaScriptЯвляется ли однопоточный язык, как добиться асинхронности?
  3. Каков порядок выполнения синхронных и асинхронных задач?
  4. Существуют ли приоритеты для асинхронных задач?

Синхронный и асинхронный

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

взять каштан

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

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

Устроившись поудобнее, начните новый день 996, и рабочие отправятся в путь!

Кипятить воду и умываться одновременно, этоАсинхронный в компьютере.

Синхронизировать в компьютереЭто непрерывное действие. До завершения предыдущего шага следующий шаг будет заблокирован. После завершения предыдущего шага можно продолжать выполнение следующего шага. Например: подождите, пока вода закипит, прежде чем пить теплую горячую воду.

Один поток может быть асинхронным?

JavaScriptЭто действительно однопоточный язык, но браузерUIЯвляется многопоточным, асинхронные задачи полагаются на потоки браузера иJavaScriptмеханизм реализации. Например,setTimeoutЭто достигается запуском функции синхронизации потока с помощью таймера браузера.

поток браузера

  1. GUIвизуализировать нить
    • Рисуйте страницы, анализируйте HTML, CSS, стройте DOM-деревья и т. д.
    • Перерисовка и перекомпоновка страниц
    • Взаимное исключение с движком JS (движок JS блокирует обновление страницы)
  2. JSрезьба двигателя
    • выполнение кода скрипта js
    • Отвечает за выполнение подготовленных событий, таких как истечение срока действия таймера или успешный и правильный асинхронный запрос.
    • Взаимное исключение с потоком рендеринга графического интерфейса
  3. поток триггера события
    • Когда соответствующее событие соответствует условию триггера, добавьте событие в конец очереди задач js.
    • Несколько событий присоединяются к очереди задач и должны ждать в очереди
  4. поток триггера таймера
    • Отвечает за выполнение событий асинхронного таймера: setTimeout, setInterval и т.д.
    • Время браузера завершается этим потоком, и событие добавляется в конец очереди задач после завершения времени.
  5. HTTPпоток запросов
    • Отвечает за асинхронные запросы
    • При отслеживании изменения статуса асинхронного запроса, если есть функция обратного вызова, поток добавит функцию обратного вызова в конец очереди задач.

Синхронный и асинхронный порядок выполнения

  1. JavaScriptРазделите задачи на синхронные задачи и асинхронные задачи, синхронные задачи входят в основную очередь, а асинхронные задачи поступают первымиEvent TableЗарегистрируйте функцию обратного вызова.
  2. при асинхронных задачахУсловие триггера выполнено, измените функцию обратного вызова сEvent Tableзатолкнуть вEvent Queueсередина.
  3. После выполнения задачи синхронизации в основном потоке система перейдет кEvent QueueПрочитайте асинхронную функцию обратного вызова в .
  4. Пока основной поток пуст, он перейдет кEvent QueueЧтение функции обратного вызова, этот процесс называетсяEvent Loop.

взять каштан

  • setTimeout(cb, 1000), через 1000 мс поместите cb в очередь событий.
  • ajax (условие запроса, cb), когда HTTP-запрос успешно отправлен, cb помещается в очередь событий.

Процесс выполнения EventLoop

Event LoopПроцесс выполнения следующий:

1.webp

Давайте рассмотрим пример, чтобы ознакомиться с описанным выше процессом.

// 下面代码的打印结果?
// 同步任务 打印 first
console.log("first");     
setTimeout(() => {        
// 异步任务 压入Event Table 4ms之后cb压入Event Queue
  console.log("second");
},0)
// 同步任务 打印last
console.log("last");     
// 读取Event Queue 打印second

Общие асинхронные задачи

  • DOMмероприятие
  • AJAXпросить
  • таймерsetTimeoutа такжеsetlnterval
  • ES6изPromise

Приоритет асинхронных задач

Продолжим рассмотрение случая:

setTimeout(() => {
  console.log(1);
}, 1000)
new Promise(function(resolve){
    console.log(2);
    for(var i = 0; i < 10000; i++){
        i == 99 && resolve();
    }
}).then(function(){
    console.log(3)
});
console.log(4)

Следуйте приведенному выше: Он может легко получить печатный результат дела:2, 4, 1, 3.

Часть определения обещания — это синхронная задача, а часть обратного вызова — асинхронная задача.

Запустив код случая в консоли, конечный результат несколько неожиданный:

2.webp

Когда я увидел этот результат, мое первое впечатление было,setTimeoutТриггер функции 1s слишком медленный, что приводит к его присоединениюEvent Queueпозже, чемPromise.then

Итак, я изменилsetTimeoutВремя обратного вызова равно 0 (минимальное время срабатывания браузера равно4ms), но результат все равно изменился.

Тогда это означает,JavaScriptАсинхронные задачи имеют приоритет.

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

JavaScriptВ дополнение к широкому разделению задач на синхронные и асинхронные также производится более детальное разделение асинхронных задач. Асинхронные задачи делятся на микрозадачи и макрозадачи.

3.webp

  • history traversalЗадача(h5исторические операции)
  • process.nextTick(nodejsасинхронная операция в)
  • MutationObserver(h5Добавлен внутрь для мониторингаDOMИзменения узла)

Макро-миссии и микро-задачи имеют свои очереди задач соответственно.Event Queue, а именно очередь макрозадач и очередь микрозадач.

Процесс выполнения цикла событий

Изучив макрозадачи и микрозадачи, давайте узнаем порядок выполнения макрозадач и микрозадач.

  1. Код начинает выполняться, создавая глобальный стек вызовов,scriptВыполнить как задачу макроса
  2. Процесс выполнения выполняется сразу после синхронной задачи, а асинхронная задача регистрируется в очереди микрозадач и очереди макрозадач в соответствии с типом асинхронной задачи.
  3. После выполнения задачи синхронизации проверьте очередь микрозадач
    • Если есть микрозадачи, выполнить все очереди микрозадач (включая новые микрозадачи, сгенерированные во время выполнения микрозадач)
    • Если микрозадач нет, проверьте очередь макрозадач, выполните первую макрозадачу, проверьте очередь микрозадач после выполнения макрозадачи и повторите вышеуказанные операции, пока очередь макрозадач не станет пустой.

ОбновитьEvent LoopСхема последовательности выполнения:

4.webp

Суммировать

На основе вышеизложенного повторите анализ текущего случая:

setTimeout(() => {
  console.log(1);
}, 1000)
new Promise(function(resolve){
    console.log(2);
    for(var i = 0; i < 10000; i++){
        i == 99 && resolve();
    }
}).then(function(){
    console.log(3)
});
console.log(4)

Процесс анализа показан на следующем рисунке:

5.webp

вопросы интервью

В конце статьи несколько классических вопросов для собеседования, вы можете проверить своиEvent Loopстепень мастерства.

Тема 1

console.log('script start');

setTimeout(() => {
    console.log('time1');
}, 1 * 2000);

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


async function foo() {
    await bar()
    console.log('async1 end')
}
foo()

async function errorFunc () {
    try {
        await Promise.reject('error!!!')
    } catch(e) {
        console.log(e)
    }
    console.log('async1');
    return Promise.resolve('async1 success')
}
errorFunc().then(res => console.log(res))

function bar() {
    console.log('async2 end') 
}

console.log('script end');

тема вторая

setTimeout(() => {
    console.log(1)
}, 0)

const P = new Promise((resolve, reject) => {
    console.log(2)
    setTimeout(() => {
        resolve()
        console.log(3)
    }, 0)
})

P.then(() => {
    console.log(4)
})
console.log(5)

тема три

var p1 = new Promise(function(resolve, reject){
    resolve("2")
})

setTimeout(function(){
    console.log("1")
},10)

p1.then(function(value){
    console.log(value)
})

setTimeout(function(){
    console.log("3")
},0)