«Хардкорный JS» понимает механизм работы JS одновременно

внешний интерфейс JavaScript
«Хардкорный JS» понимает механизм работы JS одновременно

предисловие

С начала фронтенда и до настоящего времени я прочитал много постов о механизме работы JS один за другим, но вскоре после прочтения забыл об этом, поэтому лучше сделать самому

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

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

Ссылаюсь на много информации (посты), берите суть и убирайте накипь, все в конце статьи, сами разберетесь

Пришло время сделать волну моего большого js

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

Эта статья примерно делится на следующие шаги, чтобы помочь нам широко глубоко в более четкое понимание рабочего механизма JS

  • Сначала нам нужно понять концепцию процесса и потока.
  • Во-вторых, нам нужно знать здравый смысл потока процесса браузера.
  • А потом через Event Loop, макрозадачу (macrotask) микрозадачу (microtask), чтобы увидеть, как взаимодействуют несколько потоков браузера
  • А затем использовать пример, чтобы подтвердить нашу догадку
  • Наконец, упоминается механизм работы NodeJS.

спроси у души

Операционный механизм JS имеет очень высокий процент попаданий в обычном фронтенд-интервью, будь то письменные тестовые вопросы или вопросы на собеседовании.

Говоря о рабочем механизме JS, как много вы знаете

Увидев это, вы можете ответить: Механизм работы JS очень прост, что-то вроде цикла событий, макро- и микрозадач.

Да, как фронтенд мы все понимаем, но если это действительно вопрос интервью, можете ли вы действительно ответить на него (вопрос души 🤔️)

Как бы много вы ни знали о JS, давайте остановимся здесь, допустим, вы сейчас проходите собеседование, и интервьюер просит вас объяснить механизм работы JS, обдумываете свой ответ и тратите 20 секунд (20 секунд во время собеседования уже долгое время). долго), а затем читайте дальше с ответом, кто-то однажды сказал:没有思考的阅读纯粹是消磨时间罢了, это очень хорошо (потому что я это сказал, скиньте шкуру 😄)

Есть также много студентов, которые только начали контактировать с JS.任务队列 执行栈 微任务 宏任务Эти высокие рейтинги так сбивают с толку

Далее мы подробно разберем их, чтобы вы могли их четко понять.

процесс и поток

что такое процесс

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

Официальный сайт говорит,进程даCPUНаименьшая единица распределения ресурсов

Буквально означает незавершенную программу, я понимаю ее как программу-задачу, которая может работать независимо и имеет собственное ресурсное пространство.

进程Включая запущенные программы, а также память и системные ресурсы, используемые программами.

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

что такое нить

线程даCPUНаименьшая единица планирования

线程Установлено进程На основе одноразового блока запуска программы популярная точка объяснения线程представляет собой поток выполнения в программе,进程может иметь несколько线程

Один进程Существует только один поток выполнения в单线程, то есть при выполнении программы пройденные пути программы располагаются в последовательном порядке, первые должны быть обработаны хорошо, а вторые будут выполняться

Один进程Существует несколько потоков выполнения, называемых多线程, то есть программа может запускать несколько разных программ одновременно线程выполнять различные задания, Другими словами, это позволяет одной программе создавать несколько параллельных исполнений.线程выполнять свои задачи

разница между процессом и потоком

Процесс — это наименьшая единица ресурсов, выделяемых операционной системой, а поток — наименьшая единица выполнения программы.

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

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

Планирование и переключение: переключение контекста потока выполняется намного быстрее, чем переключение контекста процесса.

Многопроцессорность и многопоточность

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

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

Почему JS однопоточный?

Однопоточный для JS, связанный с его назначением. В качестве языка сценариев браузера JavaScript в основном используется для взаимодействия с пользователем и управления DOM. Это определяет, что это может быть только один поток, иначе возникнут сложные проблемы с синхронизацией. Например, если предположить, что в JavaScript есть два потока одновременно, один поток добавляет содержимое в узел DOM, а другой поток удаляет этот узел, какой поток должен выбрать браузер?

Некоторые люди говорят, что js также имеет рабочие потоки.Да, чтобы воспользоваться вычислительной мощностью многоядерных процессоров, HTML5 предлагает стандарт Web Worker, который позволяет сценариям JavaScript создавать несколько потоков, но подпотоки полностью управляется основным потоком и не может управлять DOM.

Таким образом, этот стандарт не меняет природу однопоточности JavaScript.

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

браузер

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

Что касается внешнего интерфейса, то неизбежно иметь дело с браузером. Браузер является многопроцессорным. Возьмем, к примеру, Chrome, каждый раз, когда мы открываем вкладку, будет генерироваться процесс. Если мы используем Chrome для открытия многих вкладок , компьютер будет все больше и больше зависать.Не говоря уже о других, во-первых, он потребляет много процессора

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

  • Браузерный процесс

    • Основной процесс браузера (отвечает за координацию, главный контроль), есть только один процесс
    • Отвечает за отображение интерфейса браузера и взаимодействие с пользователем. например вперед, назад и т.
    • Отвечает за управление каждой страницей, создание и уничтожение других процессов
    • Отрисовка Bitmap (растрового изображения) в памяти, полученной в процессе рендеринга (Renderer), в пользовательский интерфейс
    • Управление сетевыми ресурсами, загрузка и т. д.
  • Процесс стороннего плагина

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

    • Также есть только один процесс для 3D-рисования и т. д.
  • процесс рендеринга (тяжелый)

    • То есть так называемое ядро ​​браузера (процесс Renderer, который внутри многопоточен)
    • Каждая вкладка имеет процесс рендеринга, который не влияет друг на друга.
    • Основная роль в рендеринге страницы, выполнением сценариев, обработки событий и т. Д.

Зачем браузерам нужна многопроцессорность?

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

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

Конечно, есть много других преимуществ многопроцессорности, но более сложные

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

Это также означает, что потребление ресурсов, таких как память, будет очень большим, а это означает, что пространство обменивается на время.

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

Кратко опишите процесс рендеринга Рендерер (тяжелый)

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

Процесс рендеринга является многопоточным. Давайте рассмотрим некоторые общие и основные потоки процесса рендеринга.

Основной поток процесса рендеринга Renderer

Поток рендеринга графического интерфейса

  • Ответственный за рендеринг интерфейса браузера, разбор HTML, CSS, DOM дерево и построить дерево RenderObject, например, макет и рисунок
    • Разобрать html-код (HTML-код по сути является строкой) и преобразовать его в узел, распознаваемый браузером, и сгенерировать DOM-дерево, то есть DOM-дерево
    • Разобрать css и сгенерировать CSSOM (дерево правил CSS)
    • Объедините дерево DOM и CSSOM для создания дерева рендеринга.
  • Когда мы изменим цвет или цвет фона некоторых элементов, страница будет перекрашена (Repaint)
  • Когда мы изменим размер элемента, страница будет перекомпоновываться (Reflow)
  • Когда страница нуждается в Repaing и Reflow, поток GUI выполняет и рисует страницу.
  • Reflow дороже, чем перерисовка, мы должны стараться избегать Reflow и Repaint.
  • Поток рендеринга GUI и поток движка JS являются взаимоисключающими.
    • Когда JS-движок запускается, поток GUI будет приостановлен (эквивалентно замораживанию)
    • Обновления графического интерфейса хранятся в очереди и выполняются, как только движок JS простаивает.

Поток движка JS

  • Поток движка JS — это ядро ​​JS, которое отвечает за обработку программ сценариев Javascript (таких как движок V8).
  • Поток движка JS отвечает за синтаксический анализ сценариев Javascript и выполнение кода.
  • Движок JS ждал поступления задач в очередь задач, а затем обрабатывал их
    • Только один браузер JS JS Engine White Runge Program, это одноретичный запуск JS
    • Существует только один поток JS, выполняющий программу JS на вкладке (процесс рендеринга) в любое время.
  • Поток рендеринга GUI и поток движка JS являются взаимоисключающими, и поток движка js блокирует поток рендеринга GUI.
    • То есть время выполнения JS, с которым мы часто сталкиваемся, слишком велико, что приводит к несогласованному рендерингу страницы, что приводит к блокировке рендеринга и загрузки страницы (то есть медленной загрузке)
    • Например, когда браузер отображает<script>метка, рендеринг GUI будет остановлен, а затем поток движка js начнет работать и выполнит код js внутри.После выполнения js поток движка js перестанет работать, а графический интерфейс продолжит рендеринг следующее содержание. Поэтому, если время выполнения js слишком велико, это приведет к зависанию страницы.

поток триггера события

  • Принадлежит браузеру, а не движку JS, управляет циклом событий и очередью задач.
  • Когда выполнение js сталкивается с привязкой события и некоторыми асинхронными операциями (такими как setTimeOut, оно также может поступать из других потоков в ядре браузера, таких как щелчки мыши, асинхронные запросы AJAX и т. д.), оно переходит в поток триггера события и добавляет соответствующее событие в соответствующий поток (например, операции таймера, добавить события таймера в поток таймера), а когда асинхронные события имеют результаты, добавить их операции обратного вызова в очередь событий и дождаться обработки потока js-движка, когда нить простаивает.
  • Когда соответствующее событие удовлетворяет условиям срабатывания и срабатывает, поток добавит событие в конец очереди ожидания и будет ждать, пока JS-движок обработает его.
  • Поскольку JS является однопоточным, события в этих ожидающих очередях должны быть поставлены в очередь для обработки механизмом JS.

синхронизированный триггерный поток

  • setIntervalа такжеsetTimeoutнить
  • Счетчик времени браузера не учитывается движком JavaScript (поскольку движок JavaScript является однопоточным, если блок заблокирован, это повлияет на точность счетчика)
  • При синхронизации и запуске отдельным потоком (по истечении времени очередь событий добавляется в поток триггера событий, ожидая выполнения механизма JS после выполнения механизма JS), этот поток является потоком триггера таймера, также называется потоком таймера.
  • W3C указывает в стандарте HTML, определяет требованияsetTimeoutВременной интервал ниже 4 мс считается за 4 мс.

Асинхронный поток HTTP-запросов

  • После подключения XMLHttpRequest через браузер открывается новый запрос потока.
  • Когда обнаружено изменение состояния, если установлена ​​функция обратного вызова, асинхронная нить будет генерировать событие изменений состояния, а обратный вызов будет введен в очередь событий, а затем выполняется двигателем JavaScript
  • Он просто выполняется, когда асинхронный http-запрос помещает событие асинхронного запроса в поток асинхронного запроса, и поэтому ответ получен (точно должно быть изменение статуса http), затем функция обратного вызова добавляется в очередь событий, ожидая потоков js-движка. выполнить

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

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

Прежде всего, вам нужно знать, что JS делится на синхронные задачи и асинхронные задачи.

Задачи синхронизации выполняются в основном потоке (главным потоком здесь является поток JS-движка), который формирует执行栈

В дополнение к основному потоку поток, инициирующий событие, управляет任务队列, пока асинхронная задача имеет работающий результат, в任务队列поместить обратный вызов события

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

Давайте посмотрим на простой фрагмент кода

let setTimeoutCallBack = function() {
  console.log('我是定时器回调');
};
let httpCallback = function() {
  console.log('我是http请求回调');
}

// 同步任务
console.log('我是同步任务1');

// 异步定时任务
setTimeout(setTimeoutCallBack,1000);

// 异步http请求任务
ajax.get('/info',httpCallback);

// 同步任务
console.log('我是同步任务2');

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

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

Сначала выполнитеconsole.log('我是同步任务1')

Затем выполните дляsetTimeoutбудет передано定时器线程,уведомлять定时器线程через 1с будетsetTimeoutCallBackЭтот обратный вызов дает事件触发线程процесс, через 1с事件触发线程получитsetTimeoutCallBackэтот обратный вызов и добавить его в事件触发线程Ожидание выполнения в очереди управляемых событий

Затем выполните http-запрос, который будет передан异步http请求线程Отправьте сетевой запрос, после того, как запрос будет успешным, он будетhttpCallbackЭтот обратный вызов обрабатывается потоком, запускающим событие,事件触发线程получатьhttpCallbackПосле этого обратного вызова добавьте его в事件触发线程Ожидание выполнения в очереди управляемых событий

Затем снова выполняетсяconsole.log('我是同步任务2')

На этом выполнение основного стека выполнения потока завершено.JS引擎线程уже бесплатно, начните事件触发线程спрашивать, спрашивать事件触发线程Есть ли какая-либо функция обратного вызова, которую необходимо выполнить в очереди событийJS引擎线程будет спрашивать, пока не будет

На данный момент мы обнаружили, что работа всех потоков в браузере едина и независима, что вполне соответствует единому принципу

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

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

Поток, инициирующий событие, заботится только об асинхронных обратных вызовах в очереди событий.

И наш поток движка JS будет выполнять только события в стеке выполнения. После того, как код в стеке выполнения будет выполнен, он прочитает события в очереди событий и добавит их в стек выполнения для продолжения выполнения. Это повторение — это то, что мы вызовЦикл событий

диаграмма

Во-первых, старт заказа стека выполнения

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

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

Если в очереди задач есть обратный вызов события, добавьте обратный вызов в конец стека выполнения и продолжите выполнение с первого шага.

Если в очереди задач нет обратного вызова события, он будет продолжать запрашивать

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

макрозадача

В ECMAScript,macrotaskтакже называетсяtask

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

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

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

Общие задачи макроса

  • блок основного кода
  • setTimeout
  • setInterval
  • setImmediate ()-Node
  • requestAnimationFrame() - браузер

микрозадача

ES6 вводит новый стандарт Promise, а браузер реализует еще одинmicrotaskКонцепция микрозадач в ECMAScript,microtaskтакже называетсяjobs

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

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

宏任务 -> 微任务 -> GUI渲染 -> 宏任务 -> ...

Общие микрозадачи

  • process.nextTick ()-Node
  • Promise.then()
  • catch
  • finally
  • Object.observe
  • MutationObserver

Простое различие между макрозадачами и микрозадачами

Прочитав объяснение приведенных выше макро- и микрозадач, вам может быть не очень ясно. Это не имеет значения. Сначала посмотрите вниз и запомните эти общие макро- и микрозадачи.

Давайте рассмотрим несколько примеров, идеи этих примеров исходят от Nuggets.云中君Ссылка на статью [14], различающая макрозадачи и микрозадачи по цвету фона, очень интуитивно понятна, я думаю, это очень интересно, поэтому этот пример также используется здесь.

Найдите пустую страницу, введите следующий код в консоли

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

Мы видим, что фон анимации выше напрямую отображается розовым цветом.Согласно вышеизложенному, браузер сначала выполнит задачу макроса, затем выполнит все микрозадачи текущего стека выполнения, а затем передаст визуализацию графического интерфейса. выше четыре строки кода относятся к одним и тем же задачам вторичного макроса, рендеринг будет выполнен после завершения всех выполнений.GUI线程Все изменения пользовательского интерфейса будут оптимизированы и объединены, поэтому визуально вы увидите, что страница станет розовой.

узнать больше

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

В приведенном выше коде страница сначала застрянет в синем, а затем превратится в черный фон.На странице написано 200 миллисекунд.Вы можете принять это за 0 миллисекунд, потому что если используется 0 миллисекунд, рендеринг браузера слишком быстрый, а запись экрана не так просто сделать. , у меня нет никаких инструментов для записи экрана и замедленного воспроизведения. Вы можете проверить это сами, и результат тот же. Самый безопасный способ - написатьindex.htmlфайл, вставьте в этот файл вышеуказанный скрипт js, затем откройте браузер, используйте консоль под гуглperformance功能查看一帧一帧的加载最为恰当,不过这样录屏不好录所以。 . .

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

увидеть снова

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

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

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

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

  • Браузер сначала выполнит задачу макроса, затем выполнит микрозадачу, сгенерированную текущим стеком выполнения, затем отобразит и затем выполнит следующую задачу макроса.
  • Микрозадачи и макрозадачи находятся не в одной очереди задач, не в одной очереди задач
    • НапримерsetTimeoutявляется задачей макроса, и ее обратный вызов события находится в очереди задачи макроса,Promise.then()Это микрозадача, и ее обратный вызов события находится в очереди микрозадач, и они не являются очередью задач.
    • Взяв в качестве примера Chrome, все, что связано с рендерингом, выполняется в процессе рендеринга.Задачи в процессе рендеринга (построение дерева DOM, парсинг js и т. д.), которые должны выполняться основным потоком, будут выполняться в основном потоке, и браузер поддерживает набор механизмов цикла событий, задачи в основном потоке будут помещены в очередь сообщений для выполнения, основной поток будет циклически выполнять очередь сообщений и брать задачи из головы для выполнения, если другие задачи генерируются во время процесса выполнения, которые должны быть выполнены основным потоком, процесс рендеринга. Другие потоки в очереди сообщений поместят задачу в конец очереди сообщений, а задачи в очереди сообщений являются задачами макросов.
    • Как возникают микрозадачи? Когда скрипт выполняется, js-движок создаст контекст выполнения для всего мира и будет поддерживать очередь микрозадач в контексте выполнения. очередь.После выполнения кода js движок проверит очередь перед выходом из глобального контекста.Если есть callback,то он будет выполнен.Если нет,то выйдет из контекста выполнения.Поэтому микрозадача раньше чем задача макроса. Задачи имеют очередь микрозадач (поскольку таймеры — это API-интерфейсы браузера, таймеры — это макрозадачи, а таймеры, встречающиеся в js, также будут помещены в очередь браузера)

На этом этапе вы все еще можете быть сбиты с толку, это не имеет значения, пожалуйста, читайте дальше

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

Сначала выполните макрозадачу, а затем оцените, есть ли после выполнения микрозадача.

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

Затем перейдите к следующей задаче макроса

Иллюстрация полного цикла событий

Во-первых, когда общий скрипт (как первая задача макроса) начинает выполняться, он делит весь код на同步任务,异步任务две части

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

Асинхронные задачи подразделяются на макрозадачи и микрозадачи.

Макро-задача попадает в Таблицу событий и регистрирует в ней callback-функцию, после завершения указанного события Таблица событий перемещает функцию в Очередь событий.

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

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

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

О обещании

new Promise(() => {}).then(), давайте посмотрим на такой промис-код

переднийnew Promise()Эта часть представляет собой конструктор, который является синхронной задачей

Назад.then()это асинхронная микрозадача, что очень важно

new Promise((resolve) => {
	console.log(1)
  resolve()
}).then(()=>{
	console.log(2)
})
console.log(3)

Вывод приведенного выше кода1 3 2

Об асинхронных/ожидающих функциях

Async/await — это по сути некая инкапсуляция, основанная на промисе, а промис — это своего рода микрозадача.

Таким образом, использование ключевого слова await похоже на Promise.then.

setTimeout(() => console.log(4))

async function test() {
  console.log(1)
  await Promise.resolve()
  console.log(3)
}

test()

console.log(2)

Вывод приведенного выше кода1 2 3 4

можно понимать как,awaitПредыдущий код, эквивалентныйnew Promiseкод синхронизации,awaitБолее поздний код эквивалентенPromise.thenасинхронный

каштановый тюлень

Во-первых, позвольте мне дать вам более интуитивную анимацию

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

Очень рекомендую всем прочитать этот пост.Очень красиво.Пошаговая анимация яркая и интуитивно понятная.Если есть время,можете испытать на себе.

Но прежде чем читать этот пост, вам нужно сначала понять механизм работы, лучше будет прочитать

Далее это относительно простой вопрос интервью, случайно найденный в Интернете, и получен результат на выходе.

function test() {
  console.log(1)
  setTimeout(function () { 	// timer1
    console.log(2)
  }, 1000)
}

test();

setTimeout(function () { 		// timer2
  console.log(3)
})

new Promise(function (resolve) {
  console.log(4)
  setTimeout(function () { 	// timer3
    console.log(5)
  }, 100)
  resolve()
}).then(function () {
  setTimeout(function () { 	// timer4
    console.log(6)
  }, 0)
  console.log(7)
})

console.log(8)

Комбинация нашего вышеупомянутого операционного механизма JS для рассмотрения этого вопроса намного проще и понятнее.

JS выполняется последовательно сверху вниз

Выполнить в test(), метод теста является синхронным и выполняется напрямую,console.log(1)распечатать 1

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

Затем выполните, в тестовом методе есть setTimeout, который является асинхронной макрозадачей, мы называем его timer2 и помещаем в очередь макрозадач.

Затем выполните обещание, новое обещание является синхронной задачей, выполните его напрямую, напечатайте 4

setTimeout в новом промисе — это асинхронная макрозадача, мы записываем обратный вызов как timer3 и помещаем его в очередь макрозадач.

Promise.then — это микрозадача, поместите ее в очередь микрозадач.

console.log(8) — это синхронная задача, выполняемая напрямую и выводящая 8

После выполнения задачи основного потока проверьте наличие Promise.then в очереди микрозадач

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

console.log(7) в очереди микрозадач — это синхронная задача, которая выполняется напрямую и выводит 7

После выполнения микрозадачи первый цикл завершается

Проверьте очередь задач макроса, есть таймер 1, таймер 2, таймер 3, таймер 4, четыре задачи макроса таймера, в соответствии с временем задержки таймера, чтобы получить порядок, который может быть выполнен, то есть очередь событий: таймер 2, таймер 4, таймер 3, таймер 1, вынимайте их по очереди и помещайте в Execute в конец стека(Интерлюдия: очередь макрозадач цикла событий браузера, то есть очередь задач макросов будет считывать только одну задачу в каждом цикле)

Выполнить timer2, console.log(3) — это задача синхронизации, выполнить ее напрямую, вывести 3

Проверяем, что микрозадач нет, второй цикл событий заканчивается

Выполнить timer4, console.log(6) — это задача синхронизации, выполнить ее напрямую, вывести 6

Проверяем, что микрозадач нет, третий цикл событий заканчивается

Выполнить timer3, задачу синхронизации console.log(5), выполнить напрямую, вывести 5

Проверяем, что микрозадач нет, четвертый цикл событий заканчивается

Выполнить timer1, задачу синхронизации console.log(2), выполнить напрямую, вывести 2

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

Результаты: 1, 4, 8, 7, 3, 6, 5, 2

Упомяните механизм работы в NodeJS.

Все вышеперечисленное относится к EventLoop браузера.

Хотя среда выполнения JavaScript в NodeJS также является V8 и также является однопоточной, все же есть некоторые отличия от производительности в браузере.

На самом деле разница между nodejs и браузерами в том, что макро задачи nodejs делятся на несколько типов, и у этих типов разные очереди задач, и разные очереди задач имеют разную последовательность, и в каждом типе микро задачи перемежаются между макро задачами

В среде узла приоритет process.nextTick выше, чем у Promise, можно просто понять, что часть nextTickQueue очереди микрозадач будет выполняться первой после завершения макрозадачи, а затем будет выполняться часть микрозадачи Promise. быть казненным.

Изображение выше взято с официального сайта NodeJS.

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

Цикл событий NodeJS относительно проблематичен

Node会先执行所有类型为 timers 的 MacroTask,然后执行所有的 MicroTask(NextTick例外)

进入 poll 阶段,执行几乎所有 MacroTask,然后执行所有的 MicroTask

再执行所有类型为 check 的 MacroTask,然后执行所有的 MicroTask

再执行所有类型为 close callbacks 的 MacroTask,然后执行所有的 MicroTask

至此,完成一个 Tick,回到 timers 阶段

……

如此反复,无穷无尽……

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

先执行一个 MacroTask,然后执行所有的 MicroTask

再执行一个 MacroTask,然后执行所有的 MicroTask

……

如此反复,无穷无尽……

Ну а по поводу анализа различных типов стадий в Node, я не буду здесь слишком много объяснять.Проверьте информацию сами.Вот краткое упоминание.Объяснение NodeJS Event Loop сложнее,чем у браузеров,так что здесь просто сравнение.

наконец

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

Уровень ограничен, добро пожаловать на ошибки

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

После прочтения этого поста рекомендую«Hardcore JS» для углубленного изучения асинхронных решений.Эта статья даст вам более глубокое понимание асинхронного программирования JS.

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

Каждый может обратить внимание на [Irregular Front End], добавить меня, добавить группу или получить некоторую информацию, а также время от времени присылать высококачественные оригиналы.

Добавить Автора

Электронная почта: 214930661@qq.com

GitHub: Github.com/isboyjc

Ссылаться на

  1. Задачи, микрозадачи, очереди и расписания — рекомендуется к прочтению

  2. Поговорим о JavaScript и браузерах — Engines and Threads

  3. Frontend Digest: глубокое погружение в то, как браузеры работают за кулисами.

  4. процесс браузера? нить? тупо не могу сказать!

  5. От входа на cnblogs.com до домашней страницы блога сада он полностью показывает, что произошло.

  6. Обязательный к прочтению интерфейс: внутренняя работа браузера

  7. Что такое цикл событий?

  8. Подробное объяснение механизма работы JavaScript: снова поговорим о цикле событий

  9. Разница между однопоточным и многопоточным

  10. Модель процесса/потока браузера и механизм запуска JS

  11. Механизм работы браузера - 2. Какие процессы включены в браузер?

  12. Нужно ли размещать JS внизу тела? Поговорим о механизме рендеринга браузера

  13. От многопроцессорности браузера до однопоточности JS — наиболее полное сочетание операционного механизма JS.

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

  15. Базовые знания Js (4) - принцип и механизм работы js

  16. На этот раз досконально изучите механизм выполнения JavaScript.

  17. Оптимизация производительности внешнего интерфейса: детализируйте реорганизацию и перерисовку рендеринга в браузере.

  18. 10 минут, чтобы понять процесс рендеринга и оптимизацию браузера