JS однопоточный
JS является однопоточным, или есть только один основной поток, что означает, что он может выполнять только один фрагмент кода за раз. На самом деле в JS нет концепции потоков, а так называемый одиночный поток относится только к многопоточности. Первоначальный дизайн JS не учитывал их.Из-за функции JS, которая не имеет параллельной обработки задач, мы называем ее «один поток».
Хотя JS выполняется в одном потоке в браузере, браузер управляется событиями.Многие действия в браузере являются асинхронными (асинхронизированными), а события создаются и помещаются в очередь выполнения. Многие асинхронные действия в браузере завершаются тем, что браузер открывает новый поток.Браузер реализует как минимум три резидентных потока:
- Поток движка JS
- Поток рендеринга графического интерфейса
- поток триггера события
JS-движок
Движок JavaScript — это виртуальная машина, которая специализируется на обработке сценариев JavaScript. Обычно она подключается к веб-браузеру. Например, наиболее известным является движок V8 браузера Chrome. Как показано на рисунке ниже, движок JS в основном состоит из двух компонентов:
- Куча - где происходит выделение памяти
- Стек — при вызове функции она формирует кадр стека (кадр)
стек выполнения
При выполнении каждой функции будет сгенерирован новый контекст выполнения (контекст выполнения).Контекст выполнения будет содержать некоторую информацию, такую как параметры текущей функции, локальные переменные и т.д., которые будут помещены в стек, контекст выполнения выполнения (выполняемый контекст) всегда находится на вершине стека. Когда функция завершает выполнение, ее контекст выполнения извлекается из стека.
Возьмем простой пример:function bar() {
console.log('bar');
}
function foo() {
console.log('foo');
bar();
}
foo();
Изменения в стеке во время выполнения:
цикл событий
Википедия определяет это так:
«Цикл событий — это программная конструкция, которая ожидает и отправляет события или сообщения в программе».
Проще говоря, это создание в программе двух потоков: один отвечает за работу самой программы, называемый «главным потоком»; другой отвечает за связь между основным потоком и другими процессами (в основном различными I /O), называемый «поток цикла событий» (можно перевести как «поток сообщений»).
Цикл событий и очередь задач
Цикл событий можно просто описать так:
- Функция помещается в стек.Когда асинхронная задача выполняется в стеке, она передается в WebAPI, а затем синхронная задача выполняется до тех пор, пока стек не станет пустым;
- В течение этого периода WebAPI завершает это событие и помещает функцию обратного вызова в CallbackQueue для ожидания;
- Когда стек выполнения пуст, цикл событий помещает задачу из очереди обратного вызова в стек и возвращается к шагу 1.
- Цикл событий реализуется средой размещения javascript (например, браузером);
- WebAPI — это созданные браузером потоки, реализованные на C++, которые обрабатывают асинхронные события, такие как события DOM, HTTP-запросы, таймеры и т. д.;
- Модель параллелизма в JavaScript основана на «цикле событий»;
- Очередь обратного вызова (очередь событий или очередь сообщений), в которой хранятся функции обратного вызова для асинхронных задач.
Рассмотрим пример асинхронного выполнения функции:
var start=new Date();
setTimeout(function cb(){
console.log("时间间隔:",new Date()-start+'ms');
},500);
while(new Date()-start<1000){};
- Функция main(Script) помещается в стек, а начальная переменная инициализируется
- стек setTimeout, стек, бросил WebAPI, время начала 500 мс;
- Цикл while помещается в стек и начинает блокироваться на 1000 мс;
- Через 500 мс WebAPI помещает cb() в очередь задач, в то время как цикл while все еще находится в стеке, а cb() ожидает;
- Еще через 500 мс цикл while выполняется и извлекается из стека, всплывает main(), стек в это время пуст, цикл событий, cb() входит в стек, log() проталкивает стек, выводит 'time interval : 1003 мс, извлекает стек, cb() извлекает из стека
Макрозадачи и микрозадачи
По сути, то, что мы сказали выше, это макрозадачи (Macrotasks), но в js есть еще и микрозадача очереди (Microtasks).
макро-задача (задача): Цикл событий имеет одну или несколько очередей задач. Источник задачи очень широк, например, ajax onload и click события. В основном различные события, которые мы часто связываем, являются источниками задачи задачи, а также операции базы данных (IndexedDB). Следует отметить, что setTimeout, setInterval и setImmediate также задача задачи источник. Подводя итог, источник задачи задачи:
- setTimeout
- setInterval
- setImmediate
- I/O
- UI rendering
Micro-Task (задание): очередь MicroTask несколько похожа на очередь задач. Оба являются в первую очередь, в первую очередь. Указанный источник задач предоставляет задачи. Разница в В контуре события есть только одна очередь микрозазки. Кроме того, сроки исполнения микрозазки также отличаются от макрозащитников.
- process.nextTick
- promises
- Object.observe
- MutationObserver
Так в чем же разница между макрозадачами и микрозадачами?
В качестве простого примера предположим, что код тега скрипта выглядит следующим образом:
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)
рабочий процесс:
Код в скрипте отображается как задача и помещается в очередь задач.
Цикл 1:
- [очередь задач: скрипт; очередь микрозадач:]
- Возьмите задачу сценария из очереди задач и поместите ее в стек для выполнения.
- promise1 указан как микрозадача, setTimeout1 указан как задача, а setTimeout2 указан как задача.
- [очередь задач: setTimeout1 setTimeout2; очередь микрозадач: promise1]
- После выполнения задачи скрипта выполняется контрольная точка микрозадачи, а promise1 очереди микрозадач вынимается и выполняется.
Цикл 2:
*[очередь задач: setTimeout1 setTimeout2; очередь микрозадач:] 4. Возьмите setTimeout1 из очереди задач, поместите его в стек для выполнения и перечислите promise2 как микрозадачу.
- [очередь задач: setTimeout2; очередь микрозадач: promise2]
- Выполните контрольную точку микрозадачи и выньте promise2 из очереди микрозадач для выполнения.
Цикл 3:
- [очередь задач: setTimeout2; очередь микрозадач:]
- Возьмите setTimeout2 из очереди задач и поместите его в стек для выполнения. 7. После выполнения задачи setTimeout2 выполнить контрольную точку микрозадачи.
- [очередь задач:; очередь микрозадач:]
Таким образом, каждый раз, когда стек выполнения цикла событий завершается, он будет продолжать выполнять соответствующую задачу микрозадачи.
Обновите рендеринг в петле события (рендеринг обновления)
Это важная часть цикла событий.Обновление рендеринга выполняется на шаге 7. Спецификация позволяет браузеру выбирать, обновлять ли представление. То есть представление может обновляться не каждый раз в цикле событий, а только при необходимости.