«Расширенный внешний интерфейс» включает в себя всестороннее сочетание многопоточности и цикла событий.

внешний интерфейс JavaScript
«Расширенный внешний интерфейс» включает в себя всестороннее сочетание многопоточности и цикла событий.

Чем больше вы знаете, тем больше вы не знаете
点赞Посмотри еще раз, аромат остался в руке, и слава

Введение

Почти в каждой книге, посвященной JS, говорится, что JS — это单线程Да, JS через очередь событий(Event Loop)способ реализации асинхронных обратных вызовов. Многим новичкам в JS непонятно, почему однопоточный JS так популярен.异步способность, поэтому я пытался получить от进程,线程точки зрения, чтобы объяснить этот вопрос.

CPU

CPU

Ядром компьютера являетсяCPU, который берет на себя все вычислительные задачи.

Это как фабрика, работающая все время.

обработать

进程

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

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

нить

线程

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

线程Как рабочий в мастерской,进程может включать несколько线程, несколько线程общий进程ресурс.

Отношения между процессором, процессом, потоком

Из вышеизложенного мы вкратце поняли ЦП, процесс, поток и кратко подытожили.

  • 进程Это наименьшая единица распределения ресурсов ЦП (наименьшая единица, которая может владеть ресурсами и работать независимо).
  • 线程Это наименьшая единица планирования процессора (поток — это единица выполнения программы, основанная на процессе, а процесс может иметь несколько потоков).
  • разные进程Связь также возможна, но по более высокой цене
  • 单线程а также多线程, оба относятся к进程единичные и множественные внутри

Браузеры являются многопроцессорными

мы уже знаемCPU,进程,线程Отношения между компьютером, каждым приложением进程, И каждое приложение будет иметь много функциональных модулей, эти функциональные модули на самом деле子进程быть реализованным. за это子进程, мы можем назвать это приложение多进程из.

Что касается браузера, браузер многопроцессорный, я открыл несколько вкладок в браузере Chrome, а затем открыл диспетчер управления окнами:

浏览器是多进程的

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

В заключение:

  • Браузеры являются многопроцессорными
  • Каждая вкладка является независимым процессом

Какие процессы включены в браузере

  • основной процесс
    • Координировать и контролировать другие дочерние процессы (создавать, уничтожать)
    • Отображение интерфейса браузера, взаимодействие с пользователем, вперед, назад, избранное
    • Нарисуйте растровое изображение в памяти, полученное в процессе рендеринга, в пользовательский интерфейс.
    • Обработка невидимых операций, сетевых запросов, доступа к файлам и т. д.
  • Процесс стороннего плагина
    • Один процесс для каждого типа подключаемого модуля, создаваемый только при использовании этого подключаемого модуля.
  • Процесс графического процессора
    • для 3D рисования и т.д.
  • 渲染进程, это то, что мы сказали浏览器内核
    • Отвечает за рендеринг страницы, выполнение скриптов, обработку событий и т. д.
    • Один процесс рендеринга на вкладку

В браузер включено так много процессов, так что же важнее всего для обычных фронтенд-операций?

ответ渲染进程, что мы часто говорим浏览器内核

Ядро браузера (процесс рендеринга)

Из предыдущей статьи мы знаем, что между процессами и потоками существует связь «один ко многим», что означает, что процесс содержит несколько потоков.

И для渲染进程Разумеется, он тоже многопоточный.Далее давайте посмотрим, какие потоки содержит процесс рендеринга.

  • GUI渲染线程
    • Отвечает за рендеринг страницы, макет и рисунок
    • Этот поток выполняется, когда страницу необходимо перерисовать и перекомпоновать.
    • Взаимное исключение с потоком движка js для предотвращения непредсказуемых результатов рендеринга.
  • JS引擎线程
    • Отвечает за разбор и выполнение javascript-скриптов.
    • Существует только один поток JS-движка (один поток)
    • Взаимоисключающий с потоком рендеринга GUI для предотвращения непредсказуемых результатов рендеринга.
  • 事件触发线程
    • Используется для управления циклом событий (щелчок мышью, setTimeout, ajax и т. д.)
    • Когда событие соответствует условию триггера, поместите событие в очередь выполнения, где находится механизм JS.
  • 定时触发器线程
    • Поток, в котором находятся setInterval и setTimeout
    • Время выполнения задачи определяется не движком JS, а синхронизированным потоком триггера.
    • По истечении времени таймера событие уведомления запускает поток
  • 异步http请求线程
    • Браузер имеет отдельный поток для обработки запросов AJAX.
    • Когда запрос завершен, если есть функция обратного вызова, событие уведомления запускает поток

Когда мы понимаем, что эти потоки включены в процесс рендеринга, мы думаем о двух вопросах:

  1. почему javascript однопоточный
  2. Почему поток рендеринга GUI взаимоисключающий с потоком движка JS

почему javascript однопоточный

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

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

Более того, если при этом манипулировать DOM, в случае многопоточности без блокировки, результат рендеринга DOM в конечном итоге будет непредсказуемым.

Почему поток рендеринга GUI и поток движка JS являются взаимоисключающими

Это связано с тем, что JS может манипулировать DOM.Если вы одновременно изменяете атрибуты элемента и визуализируете интерфейс (т.JS线程а такжеUI线程работать одновременно), Тогда элементы, полученные до и после потока рендеринга, могут оказаться несогласованными.

Поэтому, чтобы предотвратить непредсказуемые результаты рендеринга, настройки браузераGUI渲染线程а такжеJS引擎线程взаимоисключающие отношения, когдаJS引擎线程при исполненииGUI渲染线程будет приостановлено, обновления графического интерфейса будут сохранены в очереди ожиданияJS引擎线程Выполняется сразу же при бездействии.

Посмотрите, как работает механизм JS из цикла событий.

На этом этапе мы, наконец, должны войти в нашу тему, что такое цикл событий.

Сначала разберитесь с некоторыми понятиями:

  • JS делится на синхронные задачи и асинхронные задачи.
  • Задачи синхронизации выполняются в потоке движка JS, формируя执行栈
  • Поток, запускаемый событием, управляет任务队列, условие триггера асинхронной задачи достигнуто, и событие обратного вызова помещается в任务队列середина
  • 执行栈После выполнения всех задач синхронизации поток JS-движка простаивает, и система будет читать任务队列, добавьте событие обратного вызова исполняемой асинхронной задачи в执行栈, начать выполнение

Во фронтенд-разработку перейдемsetTimeout/setIntervalЧтобы указать временную задачу, она пройдетXHR/fetchотправить сетевой запрос, Далее, краткоsetTimeout/setIntervalа такжеXHR/fetchчто ты сделал

Мы знаем, что будь тоsetTimeout/setIntervalа такжеXHR/fetchкод, когда эти коды выполняются, Это сама синхронная задача, а функция обратного вызова в ней — асинхронная задача.

Когда код выполняется дляsetTimeout/setIntervalкогда на самом делеJS引擎线程уведомлять定时触发器线程, через интервал времени будет запущено событие обратного вызова, а также定时触发器线程После получения этого сообщения, по истечении времени ожидания, событие обратного вызова будет помещено в事件触发线程удалось事件队列середина.

Когда код выполняется дляXHR/fetchкогда на самом делеJS引擎线程уведомлять异步http请求线程, отправить сетевой запрос и сформулировать событие обратного вызова после завершения запроса, а также异步http请求线程После получения этого сообщения, после успешного выполнения запроса, событие обратного вызова будет помещено в事件触发线程удалось事件队列середина.

Когда наша задача синхронизации будет завершена,JS引擎线程спросит事件触发线程,существует事件队列Есть ли функция обратного вызова для выполнения, если есть, она будет добавлена ​​в стек выполнения и передана вJS引擎线程воплощать в жизнь

Объясните картинкой:

Объясним это с помощью кода:

let timerCallback = function() {
  console.log('wait one second');
};
let httpCallback = function() {
  console.log('get server data success');
}

// 同步任务
console.log('hello');
// 同步任务
// 通知定时器线程 1s 后将 timerCallback 交由事件触发线程处理
// 1s 后事件触发线程将 timerCallback 加入到事件队列中
setTimeout(timerCallback,1000);
// 同步任务
// 通知异步http请求线程发送网络请求,请求成功后将 httpCallback 交由事件触发线程处理
// 请求成功后事件触发线程将 httpCallback 加入到事件队列中
$.get('www.xxxx.com',httpCallback);
// 同步任务
console.log('world');
//...
// 所有同步任务执行完后
// 询问事件触发线程在事件事件队列中是否有需要执行的回调函数
// 如果没有,一直询问,直到有为止
// 如果有,将回调事件加入执行栈中,开始执行回调代码

В заключение:

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

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

После того, как мы получили общее представление о том, что такое стек выполнения и что такое очередь событий, давайте более подробно рассмотрим цикл обработки событий.宏任务,微任务

Что такое макро задача

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

Мы упоминали ранееJS引擎线程а такжеGUI渲染线程взаимоисключающие отношения, браузер, чтобы иметь возможность сделать宏任务а такжеDOM任务действовать упорядоченно, в宏任务После результата выполнения, в следующем宏任务Перед казнью,GUI渲染线程Начинайте работать, отрисовывая страницу.

// 宏任务-->渲染-->宏任务-->渲染-->渲染...

Основной блок кода, setTimeout, setInterval и т. д. — все макрозадачи.

Первый пример:

document.body.style = 'background:black';
document.body.style = 'background:red';
document.body.style = 'background:blue';
document.body.style = 'background:grey';

Мы можем поместить этот код в консоль браузера и выполнить следующее, чтобы увидеть эффект:

В результате мы увидим, что фон страницы мгновенно станет серым, приведенный выше код относится к тому же времени.宏任务, поэтому он запускается после завершения всех выполнений.页面渲染, при рендерингеGUI线程Все изменения пользовательского интерфейса будут оптимизированы и объединены, поэтому визуально только страница будет выделена серым цветом.

Второй пример:

document.body.style = 'background:blue';
setTimeout(function(){
    document.body.style = 'background:black'
},0)

Выполните его и посмотрите на эффект:

Я увижу, что страница отображается с синим фоном, а затем мгновенно становится черным фоном, это потому, что приведенный выше код принадлежит дважды宏任务,первый раз宏任务Выполняемый код должен сделать фон синим, затем запустить рендеринг, сделать страницу синей и запустить вторую задачу макроса, чтобы сделать фон черным.

Что такое микрозадачи

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

То есть, когда宏任务После выполнения, перед рендерингом, все сгенерированное при выполнении будет微任务все казнены.

Promise, process.nextTick и т. д. принадлежат微任务.

Первый пример:

document.body.style = 'background:blue'
console.log(1);
Promise.resolve().then(()=>{
    console.log(2);
    document.body.style = 'background:black'
});
console.log(3);

Выполните его и посмотрите на эффект:

Консоль выводит 1 3 2, потому что функция обратного вызова метода then объекта обещания выполняется асинхронно, поэтому в итоге выводится 2.

Цвет фона страницы становится черным сразу, минуя стадию синего, потому что мы устанавливаем фон на синий в макрозадаче, но выполняем микрозадачу перед рендерингом, Рендеринг, который сделал фон черным в микрозадаче

Второй пример:

setTimeout(() => {
    console.log(1)
    Promise.resolve(3).then(data => console.log(data))
}, 0)

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

// print : 1 3 2

Приведенный выше код содержит всего два setTimeout , то есть помимо основного блока кода есть еще два宏任务, первый из которых宏任务Во время выполнения выведите 1 и создайте微任务队列, так что в следующем宏任务Перед выполнением очереди выполнить первым微任务,существует微任务Во время выполнения вывод 3, после выполнения микрозадачи выполнить в следующий раз宏任务, вывод 2 во время выполнения

Суммировать

  • выполнить宏任务(Если его нет в стеке, он начнется с事件队列получено в)
  • Если встречается во время выполнения微任务, просто добавьте его в微任务в очереди задач
  • 宏任务После выполнения выполнить текущий微任务队列все в微任务(выполнять последовательно)
  • ток宏任务После завершения выполнения начните проверку рендеринга, а затемGUI线程взять на себя рендеринг
  • После рендерингаJS线程Давай, возьми на себя, начни следующий宏任务(взято из очереди событий)

Рекомендуемая серия статей

Ссылаться на