Чем больше вы знаете, тем больше вы не знаете
点赞
Посмотри еще раз, аромат остался в руке, и слава
Введение
Почти в каждой книге, посвященной JS, говорится, что JS — это单线程
Да, JS через очередь событий(Event Loop)
способ реализации асинхронных обратных вызовов.
Многим новичкам в JS непонятно, почему однопоточный JS так популярен.异步
способность, поэтому я пытался получить от进程
,线程
точки зрения, чтобы объяснить этот вопрос.
CPU
Ядром компьютера являетсяCPU
, который берет на себя все вычислительные задачи.
Это как фабрика, работающая все время.
обработать
Предположим, что фабрика имеет ограниченную мощность и может одновременно использоваться только одной мастерской. То есть, когда начинается один семинар, все остальные должны останавливаться. Подразумевается, что один ЦП может одновременно выполнять только одну задачу.
进程
Подобно производственному цеху, он представляет собой единственную задачу, с которой может справиться центральный процессор.进程
независимы друг от друга, в любое время ЦП всегда запускает进程
,разное进程
в нерабочем состоянии.
ЦП использует циклический алгоритм с разделением времени для достижения одновременной работы нескольких进程
.
нить
В мастерской может быть много рабочих, которые делят между собой все ресурсы мастерской, и они работают вместе, чтобы выполнить задачу.
线程
Как рабочий в мастерской,进程
может включать несколько线程
, несколько线程
общий进程
ресурс.
Отношения между процессором, процессом, потоком
Из вышеизложенного мы вкратце поняли ЦП, процесс, поток и кратко подытожили.
-
进程
Это наименьшая единица распределения ресурсов ЦП (наименьшая единица, которая может владеть ресурсами и работать независимо). -
线程
Это наименьшая единица планирования процессора (поток — это единица выполнения программы, основанная на процессе, а процесс может иметь несколько потоков). - разные
进程
Связь также возможна, но по более высокой цене -
单线程
а также多线程
, оба относятся к进程
единичные и множественные внутри
Браузеры являются многопроцессорными
мы уже знаемCPU
,进程
,线程
Отношения между компьютером, каждым приложением进程
,
И каждое приложение будет иметь много функциональных модулей, эти функциональные модули на самом деле子进程
быть реализованным.
за это子进程
, мы можем назвать это приложение多进程
из.
Что касается браузера, браузер многопроцессорный, я открыл несколько вкладок в браузере Chrome, а затем открыл диспетчер управления окнами:
Как показано выше, мы видим, что браузер Chrome запускает несколько процессов.
В заключение:
- Браузеры являются многопроцессорными
- Каждая вкладка является независимым процессом
Какие процессы включены в браузере
- основной процесс
- Координировать и контролировать другие дочерние процессы (создавать, уничтожать)
- Отображение интерфейса браузера, взаимодействие с пользователем, вперед, назад, избранное
- Нарисуйте растровое изображение в памяти, полученное в процессе рендеринга, в пользовательский интерфейс.
- Обработка невидимых операций, сетевых запросов, доступа к файлам и т. д.
- Процесс стороннего плагина
- Один процесс для каждого типа подключаемого модуля, создаваемый только при использовании этого подключаемого модуля.
- Процесс графического процессора
- для 3D рисования и т.д.
-
渲染进程
, это то, что мы сказали浏览器内核
- Отвечает за рендеринг страницы, выполнение скриптов, обработку событий и т. д.
- Один процесс рендеринга на вкладку
В браузер включено так много процессов, так что же важнее всего для обычных фронтенд-операций?
ответ渲染进程
, что мы часто говорим浏览器内核
Ядро браузера (процесс рендеринга)
Из предыдущей статьи мы знаем, что между процессами и потоками существует связь «один ко многим», что означает, что процесс содержит несколько потоков.
И для渲染进程
Разумеется, он тоже многопоточный.Далее давайте посмотрим, какие потоки содержит процесс рендеринга.
-
GUI渲染线程
- Отвечает за рендеринг страницы, макет и рисунок
- Этот поток выполняется, когда страницу необходимо перерисовать и перекомпоновать.
- Взаимное исключение с потоком движка js для предотвращения непредсказуемых результатов рендеринга.
-
JS引擎线程
- Отвечает за разбор и выполнение javascript-скриптов.
- Существует только один поток JS-движка (один поток)
- Взаимоисключающий с потоком рендеринга GUI для предотвращения непредсказуемых результатов рендеринга.
-
事件触发线程
- Используется для управления циклом событий (щелчок мышью, setTimeout, ajax и т. д.)
- Когда событие соответствует условию триггера, поместите событие в очередь выполнения, где находится механизм JS.
-
定时触发器线程
- Поток, в котором находятся setInterval и setTimeout
- Время выполнения задачи определяется не движком JS, а синхронизированным потоком триггера.
- По истечении времени таймера событие уведомления запускает поток
-
异步http请求线程
- Браузер имеет отдельный поток для обработки запросов AJAX.
- Когда запрос завершен, если есть функция обратного вызова, событие уведомления запускает поток
Когда мы понимаем, что эти потоки включены в процесс рендеринга, мы думаем о двух вопросах:
- почему javascript однопоточный
- Почему поток рендеринга 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线程
Давай, возьми на себя, начни следующий宏任务
(взято из очереди событий)
Рекомендуемая серия статей
- Как изящно обрабатывать исключения изображений в «Advanced Front-end»
- Анализ и реализация одностраничной маршрутизации «переднего плана»
- «Front-end Advanced» полностью понимает каррирование функций.
- Память кучи памяти стека "Front-end advanced" в JS
- «Расширенное переднее» управление памятью в JS
- Массив "Front-end advanced" вышел из строя