jsliang Job Search Series - 06 - Цикл событий

Поиск работы опрос

каталог

Чем отличается передок без закидывания от соленой рыбы?

содержание
каталог
2 Предисловие
Три однопоточных и многопоточных
Цикл четырех событий
4.1 Процесс выполнения цикла событий
4.2 requestAnimationFrame
  4.2.1 Введение в requestAnimationFrame
  4.2.2 Причина использования requestAnimationFrame
4.3 Web Worker
  4.3.1 Использование веб-воркера
  4.3.2 Передача данных веб-воркера
  4.3.3 Активный API веб-воркера
  4.3.4 Совместимость веб-воркеров
4.4 Узел и браузер
5 Сравнение двух циклов событий окружающей среды
Обучение по шести темам
6.1 Задачи синхронизации
6.2 Таймер
6.3 Таймер + Обещание
6.4 Общие
7 ссылок
7.1 Ссылки requestAnimationFrame
7.2 Ссылки на веб-воркеров
7.3 Другие ссылки

2 Предисловие

Назад к содержанию

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

Три однопоточных и многопоточных

Назад к содержанию

JavaScript — это однопоточный язык.

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

Взяв в качестве примера браузер Chrome, когда вы открываете вкладку, вы фактически создаете процесс.

В процессе может быть несколько потоков, например потоки рендеринга, потоки движка JS, потоки HTTP-запросов и т. д.

Когда вы инициируете запрос, вы фактически создаете поток, а когда запрос завершается, поток может быть уничтожен.

  • Как выглядит ядро ​​браузера?

Ядро браузера является многопоточным. Под управлением ядра потоки взаимодействуют друг с другом для поддержания синхронизации. Браузер обычно состоит из следующих резидентных потоков:

  1. Поток рендеринга графического интерфейса: анализировать HTML, CSS и т. д. Пока поток движка JavaScript выполняет сценарий, поток рендеринга GUI приостанавливается, то есть «замораживается».
  2. Поток движка JavaScript: отвечает за обработку сценариев JavaScript.
  3. синхронизированный триггерный поток:setTimeout,setIntervalЖдать. Поток триггера событий добавит подсчитанные события в конец очереди задач и будет ждать выполнения потока JS-движка.
  4. поток триггера события: Отвечает за передачу подготовленного события JS-движку для выполнения.
  5. асинхронныйhttpпоток запросов: поток, отвечающий за выполнение таких функций, как асинхронные запросы, например.Promise.then(),ajaxЖдать.
  • Почему он не предназначен для многопоточности?

Предположим, естьDOMУзел, есть темыAработай, удали этоDOM;

затем нитьBЗапустите его снова, измените этоDOMкакая-то часть.

Итак, теперь вопрос в том, кого мы слушаем?

Таким образом, он просто разработан как единый поток, который безопасен и надежен.

Даже если HTML5 выйдет позжеWeb Workerоперация не разрешенаDOMСтруктура, может завершить некоторые распределенные вычисления.

Web Workerобъясняется в этой статье

  • Зачем вам асинхронность?

На этот раз есть другая проблема.Если вызвать интерфейс (Ajax), или при загрузке изображения мы застреваем, поэтому страница не может отображаться все время?

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

Так что это время приходит асинхронно:

Когда дело доходит до некоторых операций, требующих ожидания, мы разрешаем программе продолжать работу.

Дождавшись возвращения интерфейса или картинки, я уведомлю программу о том, что закончил, и можно продолжать звонить.

Цикл четырех событий

Назад к содержанию

  • Почему есть контур событий?

ПереднийjsliangК слову о: потоки JavaScript могут выполнять только одно действие за раз.

Если вы столкнулись с некоторыми программами, которые должны ждать, например,setTimeoutПодождите, давайте остановимся.

Итак, чтобы координировать события, взаимодействие с пользователем, сценарии, рендеринг, работу в сети и т. д., JavaScript придумалЦикл событий.

  • Что такое петля событий?

JavaScript изscriptПроцесс чтения событий выполнения из «очереди задач» начинает читать, а затем непрерывно зацикливается, чтоЦикл событий.

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

Назад к содержанию

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

  1. Запустить весь скриптscriptВыполнить как задачу макроса
  2. Во время выполнения,синхронный кодвыполнять напрямую,задача макросаВойдите в очередь задач макроса,микрозадачиВойдите в очередь микрозадач.
  3. После того, как текущая задача макроса будет выполнена и удалена из очереди, проверьте список микрозадач и выполняйте их последовательно, пока все выполнения не будут завершены.
  4. Запустите браузерUIРабота с потоковым рендерингом.
  5. ПроверитьWeb Workerзадачи, выполнять их.
  6. После выполнения этого цикла макрозадач вернитесь к шагу 2 и выполняйте цикл по очереди, пока очереди макрозадач и микрозадач не опустеют.

В цикле событий есть два вида асинхронных очередей: очередь макрозадач (MacroTask) и очередь микрозадач (MicroTask).

Web Worker — это JS, работающий в фоновом режиме, независимый от других скриптов, и не влияющий на производительность страницы.

Может быть несколько очередей макрозадач и только одна очередь микрозадач.

задача макросавключать:

  • script
  • setTimeout
  • setInterval
  • setImmediate
  • I/O
  • UI rendering

микрозадачивключать:

  • MutationObserver
  • Promise.then()/catch()
  • кPromiseДругие технологии, разработанные для базы, такие какfetch API
  • Процесс сборки мусора в V8
  • Уникальный для узлаprocess.nextTick

4.2 requestAnimationFrame

Назад к содержанию

4.2.1 Введение в requestAnimationFrame

Назад к содержанию

window.requestAnimationFrame()Сообщите браузеру, что вы хотите выполнить анимацию, и попросите браузер вызвать указанную функцию обратного вызова, чтобы обновить анимацию перед следующей перерисовкой.

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

requestAnimationFrameкороткое имяrAF.

Давайте посмотрим на его использование:

<body>
  <div class="animation">动画元素</div>

  <script>
    window.onload = function() {
      const element = document.querySelector('.animation'); 
      let start;

      function step(timestamp) {
        if (start === undefined) {
          start = timestamp;
        }
        const elapsed = timestamp - start;

        // 这里使用 Math.min() 确保元素刚好停在 200px 的位置。
        element.style.transform = 'translateX(' + Math.min(0.1 * elapsed, 200) + 'px)';

        // 在两秒后停止动画
        if (elapsed < 2000) {
          window.requestAnimationFrame(step);
        }
      }

      window.requestAnimationFrame(step);
    };
  </script>
</body>

4.2.2 Причина использования requestAnimationFrame

Назад к содержанию

если мы используемsetTimeoutЧтобы добиться эффекта анимации, мы обнаружим, что на некоторых недорогих машинах возникает явление заикания и дрожания, Причины этого:

  • setTimeoutСобытие выполнения не является детерминированным. Он принадлежит очереди задач макроса.Только когда задачи в основном потоке выполняются, задачи в очереди будут вызываться, чтобы определить, начинать ли выполнение.
  • На частоту обновления влияет разрешение экрана и размер экрана, поэтому разные устройства имеют разную частоту обновления, в то время какsetTimeoutТолько фиксированный интервал времени может быть обновлен.

наверхуEvent LoopВо время процесса мы знаем, что после выполнения очереди микрозадач будет один шаг:

  • Запустите браузерUIРабота с потоковым рендерингом.

а такжеrequestAnimationFrameПросто выполните его здесь, вы не будете ждать очереди очереди задач макроса, что вызовет такие проблемы, как зависание.

4.3 Web Worker

Назад к содержанию

Web WorkerПредоставляет простой способ запуска сценариев в фоновом потоке для веб-контента.

Как мы знаем, JavaScript всегда был однопоточной средой, и мы не можем запускать два JavaScript-скрипта одновременно.

Но представьте, если бы мы могли запускать два (или более) сценария JavaScript одновременно, один для обработки пользовательского интерфейса (как он всегда использовался), а другой для обработки некоторых сложных вычислений, тогда производительность была бы лучше.

В новой спецификации HTML5 реализованоWeb WorkerЧтобы представить технологию «многопоточности» JavaScript, ее способность позволяет нам загружать и запускать еще один отдельный поток JavaScript в потоке JavaScript, работающем на главной странице.

Примечание. JavaScript по своей природе является однопоточным.Web WorkerПросто мощный API, предоставляемый браузером (средой хостинга).

4.3.1 Использование веб-воркера

Назад к содержанию

Вызвать веб-воркера:

index.js

console.log('index-同步任务');
Promise.resolve().then((res) => {
  console.log('index-Promise');
});
setTimeout(() => {
  console.log('index-setTimeout');
}, 1000);

index.html

<script>
  window.onload = function() {
    console.log('本地-同步任务');
    // 微任务之间
    Promise.resolve().then((res) => {
      console.log('本地-微任务 1');
    })
    const worker1 = new Worker('./index.js');
    Promise.resolve().then((res) => {
      console.log('本地-微任务 2');
    })

    // 宏任务之间
    setTimeout(() => {
      console.log('本地-宏任务 1');
    }, 1000);
    const worker2 = new Worker('./index.js');
    setTimeout(() => {
      console.log('本地-宏任务 2');
    }, 1000);
  };
</script>

Распечатайте результат при выполнении:

本地-同步任务
本地-微任务 1
本地-微任务 2
index-同步任务
index-Promise
index-同步任务
index-Promise
本地-宏任务 1
本地-宏任务 2
index-setTimeout
index-setTimeout

можно увидеть:

  1. выполнить первымscriptпромежуточная задача синхронизации
  2. повторно выполнитьscriptСредние и микро задачи
  3. Затем выполните работу по рендерингу UI-потока (в коде это не отражено, если интересно, можете попробовать добавитьrAF)
  4. затем выполнитьWeb Workerвнутреннее содержимое
  5. тогда сноваindex.htmlзадача макроса в
  6. Ну наконец тоWeb WorkerМакросправки в файлах

Видно, что он по-прежнему соответствуетEvent Loopобработать.

4.3.2 Передача данных веб-воркера

Назад к содержанию

index.js

onmessage = (res) => {
  // Worker 接收数据
  console.log('Worker 收到数据:', res);
  // Worker 收到数据:
  // MessageEvent {isTrusted: true, data: "查房,这里是 index.html!", origin: "", lastEventId: "", source: null, …}

  // Worker 发送数据
  postMessage('开门!这里是 index.js');
}

index.html

<script>
window.onload = function() {
  // 实例化 Worker
  const worker = new Worker('./index.js');

  // index.html 接收数据
  worker.addEventListener('message', (res) => {
  console.log('index.html 收到数据:', res);
  // index.html 收到数据:
  // MessageEvent {isTrusted: true, data: "开门!这里是 index.js", origin: "", lastEventId: "", source: null, …}
  });

  // index.html 发送数据
  worker.postMessage('查房,这里是 index.html!');

  //  终止 Worker
  worker.terminate();
};
</script>

существуетindex.html, через:

  • worker.addEventListener('message', callback). Получение данных, переданных Web Worker.
  • worker.postMessage('xxx'). Отправьте данные в Web Worker.
  • worker.terminate(). Завершить общение

существуетindex.js, через:

onmessage = (res) => {
  console.log(res); // 在 onmessage 方法接受数据
  postMessage('xxx'); // 通过 postMessage 发送数据
}

4.3.3 Активный API веб-воркера

Назад к содержанию

  • setTimeout(), clearTimeout(), setInterval(), clearInterval(): С помощью этих функций вы можетеWeb WorkerОперация синхронизации выполняется в потоке;
  • XMLHttpRequestОбъект: означает, что мы можемWeb Workerвыполнить в потокеAjaxпросить;
  • navigatorобъект: доступенppName,appVersion,platform,userAgentи другая информация;
  • locationОбъект (только для чтения): можно получить информацию о текущем URL-адресе;

Если вам нужно загрузить другие JS-скрипты:

importScripts('./index2.js', './index3.js');

// 或者

// importScripts('./index2.js');
// importScripts('./index3.js');

4.3.4 Совместимость веб-воркеров

Назад к содержанию

  • IE: версия 11
  • Грань: Версия 14+
  • Firefox: версия 51+
  • Хром: версия 56+
  • Другое: см.ссылка на канал

4.4 Узел и браузер

Назад к содержанию

почему бы не бытьЦикл событий браузераа такжеNode.js Event Loop?

Проще говоря:

  • Ваша страница отображается в браузере, а ваши данные обрабатываются в фоновом режиме (представьте себе Node.js как внутренний язык, такой как PHP и Java). Есть ли разница между ними? !

Чуть осторожнее:

  • Node.js: Node.jsEvent Loopосновывается наlibuv.libuvУже на Node.jsEvent Loopосуществленный.
  • браузер: браузерEvent Loopосновывается наСпецификация HTML5из. Спецификация HTML5 определяет толькоEvent LoopКонкретная реализация остается на усмотрение производителя браузера.

libuv— это многоплатформенная библиотека поддержки, в основном используемая для асинхронного ввода-вывода. Первоначально он был разработан для Node.js, а теперьLuvit,Julia,pyuvи другие фреймворки также используют его.Github — репозиторий libuv

Итак, мы должны объединить эти дваEvent LoopРазличай их, это разные вещи~

5 Сравнение двух циклов событий окружающей среды

Назад к содержанию

В среде браузераmicrotaskОчередь задачmacrotaskВыполнить после выполнения.

В то время как в Node.jsmicrotaskОн будет выполняться между различными этапами цикла событий, то есть после выполнения этапа он будет выполнятьсяmicrotaskОчередь задач.

Механизм временного цикла Node.js здесь не упоминается.jsliangЯ не знаком с Node и боюсь ввести в заблуждение; во-вторых, когда интервьюер спрашивает, он в основном отвечает механизму цикла событий браузера и иногда упоминает об этом.Event LoopРазделенный на цикл событий браузера и цикл событий узла, это небольшая точка.

Обучение по шести темам

Назад к содержанию

Перед обучением поговорим об объеме тестовых вопросов:

  • Синхронизировать задачу: В случае прямого исполнения не беспокойтесь о 3721.
  • задача макроса:script,setTimeout
  • микрозадачи:Promise.then(),async/await

На этом пока все, не могу ошибиться!

6.1 Задачи синхронизации

Назад к содержанию

function bar() {
  console.log('bar');
}

function foo() {
  console.log('foo');
  bar();
}

foo();

Каков результат этого контента?

  • foo -> bar

Детали объяснять не нужно.

6.2 Таймер

Назад к содержанию

console.log("1");

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

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

console.log("4");
  • Очередь задач макроса:script,setTimeout(2),setTimeout(3)
  • Очередь микрозадач: нет

Итак, вывод:

1
4
2
3

6.3 Таймер + Обещание

Назад к содержанию

  • Вопрос 1: Пожалуйста, выведите следующий код.
console.log('script start');

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

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

console.log('script end');

scriptПод задачей макроса:

  • задача макросаsetTimeout
  • микрозадачи.then(promise1)

Поэтому сначала выполните синхронный код, сначала выведите:script start -> script end.

Затем вызовите микрозадачу, выводpromise1,Будуthen(promise2)Ставьте микрозадачи.

Снова вызовите микрозадачу, поставивpromise2выход.

Наконец, вызов макросаsetTimeout, выходsetTimeout.

Итак, порядок вывода:

script start
script end
promise1
promise2
setTimeout

  • Вопрос 2: Пожалуйста, выведите следующий код.
Promise.resolve().then(function promise1() {
  console.log('promise1');
})

setTimeout(function setTimeout1() {
  console.log('setTimeout1')
  Promise.resolve().then(function promise2() {
    console.log('promise2');
  })
}, 0)

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

scriptПод задачей макроса:

  • Задача синхронизации: нет
  • Микрозадачи:Promise.then(promise1)
  • Задача макроса:setTimeout(setTimeout1),setTimeout(setTimeout2)

Так что сначала зайдите в задачу синхронизации, обнаружите, что ее нет, проигнорируйте.

Затем перейдите к микрозадачамPromise.then(promise1), выходpromise1.

Затем запустите задачу макроса, сначалаsetTimeout(setTimeout1):

  • Синхронизация задач:console.log('setTimeout1')
  • Микрозадачи:Promise.then(promise2)
  • Задача макроса:setTimeout(setTimeout2)(обратите внимание, что задача макроса здесь целостная)

Итак, сначала перейдите к задаче синхронизации, выведитеsetTimeout1.

Затем переходим к микрозадаче и выводимpromise2.

Затем запустите задачу макросаsetTimeout(setTimeout2).

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

Итак, порядок вывода:

promise1
setTimeout1
promise2
setTimeout2

  • Вопрос 3: Пожалуйста, выведите следующий код.
setTimeout(function() {
  console.log(4);
}, 0);

const promise = new Promise((resolve) => {
  console.log(1);
  for (var i = 0; i < 10000; i++) {
    i == 9999 && resolve();
  }
  console.log(2);
}).then(function() {
  console.log(5);
});

console.log(3);

scriptВниз:

  • Синхронизация задач:console.log(1),console.log(2),console.log(3).
  • Микрозадачи:Promise.then()(Подождите до 9999, чтобы добавить его)
  • задача макросаsetTimeout

Итак, давайте сначала перейдем к задаче синхронизации, обратите внимание, когда мыnew Promsie(), внутренний код будет выполняться так же, как и синхронная задача, и.then()существуетresolve()добавляется в микрозадачу.

Итак, выведите сначала1 -> 2 -> 3.

Затем запускайте микрозадачиPromise.then(), поэтому выведите 5.

Наконец, запустите задачу макросаsetTimeout, выход 4.

Последовательность результатов такова:

1
2
3
5
4

6.4 Общие

Назад к содержанию

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


  • Вопрос 1: Пожалуйста, выведите следующий код.
setTimeout(function () {
  console.log('timeout1');
}, 1000);

console.log('start');

Promise.resolve().then(function () {
  console.log('promise1');
  Promise.resolve().then(function () {
    console.log('promise2');
  });
  setTimeout(function () {
    Promise.resolve().then(function () {
      console.log('promise3');
    });
    console.log('timeout2')
  }, 0);
});

console.log('done');

результат:

start
done
promise1
promise2
timeout2
promise3
timeout1

  • Вопрос 2: Пожалуйста, выведите следующий код.
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

  • Вопрос 3: Пожалуйста, выведите следующий код.
console.log(1);

setTimeout(() => {
  console.log(2);

  new Promise((resolve) => {
    console.log(3);
  }).then(() => {
    console.log(4);
  });
}, 200);

new Promise((resolve) => {
  console.log(5);
  resolve();
}).then(() => {
  console.log(6);
});

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

setTimeout(() => {
  console.log(8);

  new Promise(function (resolve) {
    console.log(9);
    resolve();
  }).then(() => {
    console.log(10);
  });
}, 100);

new Promise(function (resolve) {
  console.log(11);
  resolve();
}).then(() => {
  console.log(12);
});

console.log(13);

выход:

1
5
11
13
6
12
7
8
9
10
2
3

7 ссылок

Назад к содержанию

7.1 Ссылки requestAnimationFrame

Назад к содержанию

7.2 Ссылки на веб-воркеров

Назад к содержанию

7.3 Другие ссылки

Назад к содержанию


репозиторий документации jsliang предоставляетсяЛян ЦзюньронгиспользоватьCreative Commons Attribution-NonCommercial-ShareAlike 4.0 Международная лицензияЛицензия.
на основеGitHub.com/l ian Jun Ron…Создание работ выше.
Права на использование, отличные от разрешенных в настоящем Лицензионном соглашении, могут быть получены отCreative Commons.org/licenses/не…получено в.