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

Событийный цикл: микрозадачи и макрозадачи

Поток выполнения в браузере, равно как и в Node.js, основан на событийном цикле.

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

В этой главе мы сначала разберём теорию, а затем рассмотрим её практическое применение.

Событийный цикл

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

Общий алгоритм движка:

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

…Но, возможно, мы хотим что-нибудь показать во время выполнения задачи, например, индикатор выполнения.

Так будет красивее:

Пример 3: делаем что-нибудь после события

В обработчике события мы можем решить отложить некоторые действия, пока событие не «всплывёт» и не будет обработано на всех уровнях. Мы можем добиться этого, обернув код в setTimeout с нулевой задержкой.

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

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

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

Какой здесь будет порядок?

Более подробное изображение событийного цикла выглядит так:

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

Это важно, так как гарантирует, что общее окружение остаётся одним и тем же между микрозадачами – не изменены координаты мыши, не получены новые данные по сети и т.п.

Итого

Более подробный алгоритм событийного цикла (хоть и упрощённый в сравнении со спецификацией):

Чтобы добавить в очередь новую макрозадачу:

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

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

Для добавления в очередь новой микрозадачи:

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

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

Для длительных тяжёлых вычислений, которые не должны блокировать событийный цикл, мы можем использовать Web Workers.

Это способ исполнить код в другом, параллельном потоке.

Web Workers могут обмениваться сообщениями с основным процессом, но они имеют свои переменные и свой событийный цикл.

Web Workers не имеют доступа к DOM, поэтому основное их применение – вычисления. Они позволяют задействовать несколько ядер процессора одновременно.

Задачи

Что код выведет в консоли?

Давайте разберем что здесь происходит.

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

Источник

Задачи, микрозадачи, очереди и планы

Предлагаю вашему вниманию перевод статьи «Tasks, microtasks, queues and schedules» Джейка Арчибальда (Jake Achibald), занимающего должность Developer Advocate for Google Chrome.

Когда я сказал своему коллеге Мэту Ганту, что подумываю о написании статьи об очерёдности микрозадач и порядке их исполнения внутри событийного цикла браузера, он сказал «Джейк, буду честен, я об этом читать не стану». Что ж, я всё же написал, поэтому откиньтесь на спинку кресла и давайте вместе в этом разберёмся, ладно?

На самом деле, если вам будет проще посмотреть видео, есть замечательное выступление Филиппа Робертса на JSConf, которое рассказывает о событийном цикле – оно не покрывает микрозадачи, но в остальном является отличным вступлением в тему. В любом случае, погнали…

Давайте рассмотрим следующий код на JavaScript:

Почему так происходит

Для более точного понимания процесса нужно сначала представить как событийный цикл обрабатывает задачи и микрозадачи. На первый раз это может показаться слишком сложным. Глубокий вдох…

Каждый «поток» имеет собственный событийный цикл, а значит и каждый веб-воркер, так что они могут выполняться независимо, тогда как все окна с одного домена (по правилу same origin) делят между собой один и тот же событийный цикл, ведь они могут синхронно коммуницировать между собой. Событийный цикл работает постоянно, исполняя поставленные в очередь задачи. Задачи выполняются последовательно и не могут пересекаться. Ладно-ладно, не уходите…

Задачи планируются таким образом чтобы браузер мог из их дебрей ступить на землю JavaScript/DOM и быть уверенным что эти действия происходят поочерёдно. Обработка колбека события щелчка мыши требует планирования задачи, так же как и разбор HTML и setTimeout из примера выше.

Микрозадачи обычно планируются для вещей, который должны исполняться моментально после текущего исполняемого сценария. Например, реагирование на пачку действий или для того, чтобы сделать что-то асинхронно без необходимости терять производительность на пустом месте из-за полностью новой задачи. Очередь микрозадач развёртывается в конце каждой полной задачи, а также после колбеков в случае если никакой другой JavaScript не находится на стадии исполнения. Любые дополнительные микрозадачи, поставленные в очередь во время развёртывания очереди микрозадач, добавляются в конец очереди и тоже обрабатываются. Микрозадачи включают в себя колбеки Mutation observer и промисов, как в примере выше.

Прим. переводчика: в этом месте у автора в оригинале вставлена великолепная наглядная презентация работы планироващика JavaScript, однако повторить это на Хабре у меня едва ли имеется техническая возможность, за сим отправляю любознательного читателя на страницу оригинала.

Да, я и правда сделал пошаговую анимированную диаграмму. Как вы провели свою субботу, наверняка гуляли где-то на свежем воздухе с друзьями? Что ж, я – нет. На случай, если что-то не ясно в моём обалденном UI, попробуйте пощёлкать стрелочки вправо-влево.

Что неправильно в некоторых браузерах?

Как понять когда используются задачи, а когда – микрозадачи

Точный способ – посмотреть спецификацию. Например, шаг 14 setTimeout ставит в очередь задачу, тогда как в спецификации фиксирования мутации шаг 5 создаёт микрозадачу.

Теперь давайте взглянем на более комплексный пример. В зале кто-то сконфужено вскрикнет «Нет, они не готовы!». Не обращайте внимания, вы готовы.

Первый уровень: Схватка с Боссом

Следующая задачка могла бы показаться мне сложной до того как я написал этот пост. Вот небольшой кусок HTML:

Попробуйте подумать прежде чем перейдёте к ответу. Подсказка: логи могут выводиться больше раза.

Испытание

Прим. переводчика: у автора в этом месте в блоге есть интерактивный DOM элемент (прямая ссылка) на котором можно воочию проверить поведение вашего браузера.

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

Кто прав?

Обработка события «click» это задача. Колбеки Mutation observer и промиса ставятся в очередь как микрозадачи. Колбек setTimeout это задача. (Прим. переводчика: тут снова интерактивная диаграмма, поясняющая пошагово принцип работы приведённого ранее кода, рекомендую взглянуть.)

Так что правильно ведёт себя Chrome. Для меня в новость было узнать что микрозадачи развёртываются после колбеков (если только это не часть выполнения другого сценария JavaScript), я думал что их развёртывание ограничено лишь окончанием выполнения задачи. Это правило описано в спецификации HTML по вызову колбеков:

Execution of a Job can be initiated only when there is no running execution context and the execution context stack is empty…
— ECMAScript: Jobs and Job Queues

…хотя «can be» в контексте HTML носит характер «must be», т.е. «обязан».

Читайте также:  Что такое клиническая фармакология

Что недопоняли браузеры?

Firefox и Safari верно опустошают очередь микрозадач между обработчиками щелчков, как видно по колбекам мутации, но промисы ставятся в очередь иначе. Это можно было бы простить, особенно учитывая туманность связи между заданием («jobs») и микрозадачей, однако я ожидал что они выполнятся между обработчиками. Заявка на Firefox. Заявка на Safari.

Мы уже поняли, что Edge ставит промисы в очередь неверно, но он также не стал опустошать очередь микрозадач между обработчиками щелчков, вместо этого очередь развернулась лишь после вызова всех обработчиков, что объясняет единственный вывод mutate после обоих click в журнале. Это ошибка.

Злой брат Босса с Первого уровня

Блин! А что если к предыдущему примеру добавить:

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

Испытание

И я не перестаю получать различные результаты в Chrome, я уже сто раз обновлял эту таблицу думая что до этого по ошибке проверял в Canary. Если у вас в Chrome другие результаты, скажите мне в комментариях на какой вы версии.

Почему теперь по-другому?

Прим. переводчика: в этом месте ещё один последний раз автор даёт нам возможность насладиться визуализацией чудес инженерной мысли браузеростроителей (ссылка, опять-таки, прямая).

После того как каждый из обработчиков щелчка вызван…

Разве это важно?

Ещё бы, это будет съедать вас изнутри (уф). Я столкнулся с этим когда попытался создать лаконичную обёртку над IndexedDB, использующую промисы вместо ужасных объектов IDBRequest. С ней IDB почти стал мне приятен.

Когда в IDB срабатывает событие успешности, объект транзакции становится неактивным после передачи управления (шаг 4). Если я создам промис, который решается во время возбуждения этого события, обработчики должны бы исполниться до шага 4 пока транзакция ещё активна, однако этого не происходит ни в одном браузере кроме Chrome, из-за чего библиотека становится как бы бесполезной.

В Firefox с этим можно справиться, ведь полифилы промисов, такие как es6-promise, используют Mutation observers для колбеков, которые есть не что иное как микрозадачи. Safari при этом исправлении вступает в состояние гонки, но дело, скорее всего, в их поломанной реализации IDB. К сожалению IE/Edge на данный момент не подлежит исправлению, так как события мутаций не происходят после колбеков.

Остаётся лишь надеяться что в этом вопросе мы когда-то сможем наблюдать взаимозаменяемость.

Мы сделали это!

Источник

Как эмулировать многопоточность в JavaScript

Авторизуйтесь

Как эмулировать многопоточность в JavaScript

Как создать иллюзию многопоточности, используя JavaScript? Работая одновременно с двумя программами, операционная система резервирует для каждой отдельный участок памяти и виртуальное пространство адресов, определённое в BDT. ОС может переключаться между двумя исполняемыми процессами, обрабатывая каждый определённое количество времени. Система ставит на паузу один процесс, сохраняя его адреса, и продолжает работу с другим с точки сохранения.

Посмотрим, как можно создать в JavaScript несколько потоков, подобно тому, как это делают в Java.

17–19 декабря, Онлайн, Беcплатно

Для этого мы используем events — планирование исполнения разных участков кода на определённое время. Этот метод применения асинхронности в JavaScript называется цикл событий. В этой статье вы узнаете принципы работы этой системы, написав собственный движок JS. Практика — лучший способ понять, как язык обрабатывает очередь задач с помощью циклов.

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

JS использует для асинхронной обработки задач концепцию циклов событий. Этот подход требует прикрепления к событиям обработчиков таким образом, чтобы при наступлении событий исполнялся прикреплённый к ним код. Прежде чем двинуться дальше, давайте рассмотрим, как работает движок JS.

Движок JS состоит из стека, кучи и очереди задач.

Это структура, похожая по строению на массив, отслеживающая исполняемые функции.

Прежде чем обработать код функции, движок JS помещает её адрес в стек вызова. На самом низком уровне существуют регистры EAX, EBX, ECX, ESP, EIP. Они используются центральным процессором для временного хранения переменных и исполнения загруженных в память программ. EAX и EBX используются для вычислений, ECX обрабатывает счётчики (например в цикле for). ESP (указатель стека) содержит текущий адрес стека, EIP (указатель инструкции) — адрес исполняемой программы.

Это грубый набросок того, как выглядит память во время исполнения программы.

Когда работа функции a завершается, адрес (7) выталкивается из стека в EIP, и исполнение программы продолжается с этого адреса.

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

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

Очередь задач

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

Цикл событий — это постоянный процесс, который проверяет стек вызова, и если стек пуст, переходит к исполнению инструкций из очереди задач.

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

Полезные книги и статьи по теме (на английском языке):

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

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

Однако работа очереди задач несколько сложнее. Запланированные действия разбиты на микрозадачи и макрозадачи.

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

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

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

Из этого кода видно, что микрозадачи выполняются раньше макрозадач:

Запустив его, мы получим следующее.

Как уже упоминалось, в одной итерации цикла событий обрабатываются макрозадачи, а затем очередь всех микрозадач. Можно возразить, что setTimeout должен быть обработан первым, так как макрозадача выполняется до очистки очереди микрозадач. А в приведённом скрипте до вызова setTimeout не запланировано никаких макрозадач.

Это действительно так. Однако в JS код не запускается до наступления события. Это событие запланировано в очереди как макрозадача.

Начиная обработку, движок JS выбирает первую макрозадачу из очереди и выполняет обработчик обратного вызова:

— “Asynchronous Programming in Javascript CSCI 5828: Foundations of Software Engineering Lectures 18–10/20/2016” by Kenneth M. Anderson.

Следуя по стопам Джейка Арчибальда, эмулируем цикл событий. В данном случае это будет разделение на макро- и микрокоманды, реализованное посредством JS-кода.

Затем мы определяем функции, транслирующие макро- и микрозадачи: setMicro (4), setMacro (5), runScript (6) и setTimeout (7). Эти функции принимают в качестве параметра обратный вызов fn и помещают fn в соответствующую очередь.

Читайте также:  Что такое дольмен в лазаревском

Ранее мы рассмотрели примеры макро- и микрозадач. Упомянутые функции определённым образом определяют макро- и микрозадачи при вызове. В нашем случае мы просто помещаем обратный вызов fn в соответствующую очередь. setMicro является функцией микрозадачи, поэтому её обратный вызов помещается в очередь микрозадач. Функцию setTimeout мы переопределили, поэтому при исполнении кода будет обработана наша версия.

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

Чтобы посмотреть, как это работает на практике, попробуем запустить наш JS-код.

Теперь давайте используем наш код example.js, с помощью которого мы демонстрировали макро- и микрозадачи, но с некоторыми изменениями:

Таким образом, мы ожидаем следующее:

Чтобы запустить код в нашем собственном движке JS, мы транслируем код следующим образом:

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

Источник

Русские Блоги

Механизм выполнения движка JS: Исследуйте EventLoop (включая Macro Task и Micro Task)

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

Сначала объясните существительные в следующих темах: (Вы поймете эти понятия после прочтения этой статьи)

Event Loop: Event Loop
Micro Task: Micro Task
Макро задача: Макро задача

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

1. Почему JS является однопоточным языком.

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

2. Что такое очередь задач?

2.1 Синхронные задачи и асинхронные задачи

(1) Все синхронные задачи выполняются в главном потоке, образуяСтек выполнения(execution context stack)。

(2) В дополнение к основному потоку существует также «очередь задач» (очередь задач). Пока асинхронная задача имеет результат выполнения, событие помещается в «очередь задач».

(3) Как только все синхронные задачи в «стеке выполнения» будут выполнены, система прочитает «очередь задач», чтобы увидеть, какие в ней события. Затем соответствующие асинхронные задачи завершают состояние ожидания, входят в стек выполнения и начинают выполнение.

(4) Основной поток продолжает повторять третий шаг выше.

2.2 Модель исполнения двигателя JS

Говоря макроскопически, Macrotask войдет в Macro Task Queue, а Microtask войдет в Micro Task Queue. Микро Задача разделена на две очереди. «Микро Задача» Promise Ждите Microtask. И «Tick Task Queue» посвящена хранилищу process.nextTick Задача. Теперь давайте взглянем на классификацию норм.

Сказал, ‘После завершения каждой макрозадачи все микрозадачи должны быть очищены.The. Движок будет проходить через очередь задач макроса. Для каждой макрозадачи он будет проходить через все задачи, которые выполняют очередь задач галочки, а затем все задачи очереди микрозадач. nextTick Будет лучше чем Promise Исполнение)

3. Event Loop

Основной поток читает события из «очереди задач». Этот процесс является непрерывным, поэтому весь рабочий механизм также называется Event Loop.

Модификация: на следующей картинке есть ошибки, добавлен process.nextTick вВ конце текущего «стека выполнения» фактически нет так называемой очереди TickTask, только очередь MacroTask и очередь микро-задач. Очередь TickTask на рисунке просто для лучшего понимания, или мы понимаем этоКонец текущего «стека выполнения».

Блок-схема выполнения кода в трех очередях задач выглядит следующим образом:

Давайте проверим это на примере:

Анализ выглядит следующим образом

Запустите выполнение кода, выведите main1, process.nextTick в очередь TickTask, установите setTimeout в очередь MacroTask, новое обещание вывода выполнения Promise, затем метод в очередь MicroTask, затем последнюю строку кода console.log выведите main2

После выполнения текущей макро-задачи начните очищать микро-задачу, сначала очистите очередь TickTask, выполните console.log (‘process.nextTick1’); output ‘process.nextTick1; затем очистите очередь MicroTask и выполните console.log (‘ обещание тогда ‘); Обещание затем вывод, все микрозадачи очищаются.

В-четвертых, цикл событий в Node.js

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

Согласно приведенному выше рисунку, рабочий механизм Node.js работает следующим образом.

(1) Движок V8 анализирует скрипт JavaScript.

(2) После анализа кода вызовите Node API.

(3)библиотека libuvОтвечает за выполнение Node API. Он назначает разные задачи различным потокам для формирования цикла обработки событий (цикла обработки событий) и возвращает асинхронный результат выполнения задачи в механизм V8.

(4) Механизм V8 возвращает результат пользователю.

Node.js также предоставляет два других метода, связанных с «очередью задач»:process.nextTickиsetImmediate, Они могут помочь нам углубить наше понимание «очереди задач».

В приведенном выше коде, так какФункция обратного вызова, указанная методом process.nextTick, всегда находится в Текущий «стек исполнения» Хвост курка,Таким образом, функция A не только выполняется до истечения времени ожидания функции обратного вызова, указанного в setTimeout, но и функция B также выполняется до истечения времени ожидания. Это показывает, чтоЕсли существует несколько операторов process.nextTick (независимо от того, являются ли они вложенными), все они будут выполняться в текущем «стеке выполнения».

Таким образом, мы получаем важное различие между process.nextTick и setImmediate:Множественные операторы process.nextTick всегда выполняются один раз в текущем «стеке выполнения», и для выполнения нескольких setImmediate может потребоваться несколько циклов.

Фактически, это именно та причина, по которой метод setImmediate был добавлен в Node.js версии 10.0, в противном случае рекурсивный вызов process.nextTick, как показано ниже, будет бесконечным, и основной поток вообще не будет читать «очередь событий»!

Фактически, теперь, если вы напишите рекурсивный process.nextTick, Node.js выдаст предупреждение с просьбой изменить setImmediate.

Кроме того, поскольку функция обратного вызова, указанная в process.nextTick, запускается в этом «цикле событий», а setImmediate указывается в следующем «цикле событий», ясно, что первый всегда происходит раньше, чем последний, и эффективность выполнения Также высокий (потому что нет необходимости проверять «очередь задач»).

6 фаз событийного цикла в узле

Цикл событий nodejs разделен на 6 этапов, роль каждого этапа в следующем ( process.nextTick() Будет выполнено в конце 6 этапов Вторая половина статьи будет подробно проанализирована process.nextTick() Как обратный вызов события вводится в цикл событий, только из uv_run() Не найден process.nextTick() Как это связано):

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

Источник

Русские Блоги

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

Поэтому, если весь код выполняется синхронно, это вызовет очень серьезную проблему. Например, если мы хотим получить некоторые данные с удаленного конца, следует ли нам продолжать зацикливать код, чтобы судить, получили ли мы возвращаемый результат?Это все равно что ходить в ресторан за едой. Нельзя сказать, что тебя будут избивать, если ты пойдешь на кухню после того, как призовешь людей готовить.
Таким образом, существует концепция асинхронных событий: зарегистрируйте функцию обратного вызова, например, отправьте сетевой запрос, мы сообщим основной программе уведомить меня после получения данных, а затем мы можем выполнить другие вещь.
Затем, после асинхронного завершения, мы уведомим нас, но в это время программа может делать другие вещи, поэтому даже если асинхронное завершение завершено, вам нужно дождаться стороны и дождаться, пока программа не будет работать. Чтобы увидеть, какая асинхронность была завершена, вы можете выполнить ее.
Например, если вы столкнулись с автомобилем, если водитель прибывает первым, но у вас все еще есть с чем поработать, водитель не сможет сначала ехать в машине. Вы должны подождать, пока не сядете в машину после решения вопроса.

Разница между микрозадачами и макрозадачами

Это все равно что идти в банк, чтобы заниматься бизнесом.
Как правило, такие слова, как «Ваш номер XX, а перед ним XX человек».

Читайте также:  русские боги до крещения руси список

Кроме того, во время выполнения макро-задачи вы можете добавить несколько микро-задач. Так же, как при ведении дел у прилавка, пожилой человек перед вами может внести депозит. После обработки депозита кассир попросит старика вернуть Есть ли другой бизнес, которым нужно заниматься? В это время старик думал об этом: «В последнее время очень много P2P-громов, вы хотите выбрать более стабильный финансовый менеджмент?» Конечно, я не могу сказать старику: «Ты поднимешь номер и вернешься в очередь».
Так что ваша очередь заниматься бизнесом, потому что старик временно добавил «Управление благосостоянием«И оттолкнул.
Может быть, старик все еще хочет сделатьПолучить другую кредитную карту? илиКупите еще памятные монеты
Независимо от того, что вам нужно, если кассир может ей помочь, они будут делать это до того, как заняться вашим бизнесом. Это можно рассматривать как микро-задачи.

Это значит: твой дядя всегда будет твоим дядей
Если текущая микрозадача не завершена, следующая макрозадача не будет выполнена.

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

setTimeout Существует как макро-задача, и Promise.then Это типичная микрозадача, и порядок выполнения вышеуказанного кода выводится в соответствии с серийным номером.

Все асинхронное, которое будет введено, относится к части кода в обратном вызове события.
означает new Promise Код, выполняемый в процессе создания экземпляра, синхронизируется, и then Зарегистрированный обратный вызов выполняется асинхронно.
После завершения выполнения синхронного кода вернитесь назад, чтобы проверить, завершена ли асинхронная задача, и выполните соответствующий обратный вызов, и микро-задача будет выполнена до выполнения макро-задачи.
Итак, я получил приведенный выше вывод 1、2、3、4 。

+ Часть представляет код, который выполняется синхронно

первоначально setTimeout Таймер был установлен (эквивалентно взятию числа), а затем некоторые из них добавляются в текущий процесс Promise Обработка (добавление бизнеса временно).

П.С. вОбещание / A + технические характеристикив Promise Реализация может быть микро-задачей или макро-задачей, но общее согласие указывает (по крайней мере, Chrome Это сделано), Promise Должен принадлежать к микро-миссионерскому лагерю

Макро задача

# браузер Node
I/O
setTimeout
setInterval
setImmediate
requestAnimationFrame

Некоторые места будут перечислены UI Rendering Сказать, что это тоже макро-задача, но я читаюДокумент спецификации HTMLПозже выяснилось, что это явно шаг операции, параллельный микрозадаче.
requestAnimationFrame Давайте назовем это макрозадачей, requestAnimationFrame вОпределение MDNЧтобы выполнить операцию до перерисовки следующей страницы, перерисовка также существует как шаг макро-задачи, и этот шаг позже, чем выполнение микро-задачи

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

# браузер Node
process.nextTick
MutationObserver
Promise.then catch finally

Что такое Event-Loop

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

Каждый раз после завершения бизнеса кассир спрашивает текущего клиента, есть ли какие-либо другие предприятия, которые необходимо обработать.(Проверьте, есть ли какие-либо микро-задачи для обработки)
И после того, как клиент четко сообщил, что ничего не было, кассир пошел, чтобы проверить, есть ли люди, которые ждут, чтобы заняться бизнесом.(Завершите эту макро-задачу, проверьте, есть ли какая-либо макро-задача для обработки)
Этот процесс проверки является непрерывным, и он будет выполняться после завершения каждой задачи, и такая операция называется Event Loop 。(Это очень простое описание, на самом деле это будет намного сложнее)

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

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

И чтобы быть ясно, Event Loop Он просто отвечает за то, чтобы сказать вам, какие задачи выполнять или какие обратные вызовы вызваны, реальная логика все еще выполняется в процессе.

Производительность в браузере

Выше кратко объясняется разница между этими двумя задачами, и Event Loop Роль, тогда какова производительность в реальном браузере?
Первое, что нужно прояснить, это то, что макро-задача должна выполняться после микро-задачи (потому что микро-задача на самом деле является одним из шагов макро-задачи)

Предположим, есть некоторые DOM Структура:

Некоторые небольшие сюрпризы

Это все равно что ходить в ресторан за едой. Официант три раза кричал. XX говяжья лапша не означает, что она даст вам три миски говяжьей лапши.
См вышеTasks, microtasks, queues and schedulesЕсть анимированная версия статьи

Производительность в узле

Узел также однопоточный, но обрабатывает Event Loop Браузер немного отличается от браузера, вотУзел официальной документацииАдрес.

Только на уровне API Node добавил два новых метода, которые можно использовать: микрозадача process.nextTick И макро задачи setImmediate 。

Разница между setImmediate и setTimeout

Определение в официальном документе, setImmediate На этот раз Event Loop Вызывается после завершения выполнения.
setTimeout Выполняется после расчета времени задержки.

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

Но если вы добавите код позже, вы можете гарантировать setTimeout Обязательно будет в setImmediate Срабатывает раньше:

Если в другой задаче макроса, она должна быть setImmediate Сначала выполните:

process.nextTick

Как уже упоминалось выше, это можно рассматривать как аналог Promise и MutationObserver Реализация микро-задачи может быть вставлена ​​в любое время во время выполнения кода nextTick И будет гарантированно выполнено до начала следующей макро-задачи.

Одним из наиболее распространенных примеров использования является работа некоторых классов привязки событий:

Поскольку приведенный выше код создан Lib Объект выполняется синхронно и отправляется сразу после завершения создания экземпляра. init Событие.
и основная программа на внешнем уровне еще не начала выполняться lib.on(‘init’) Этот шаг прослушивания событий.
Следовательно, при отправке события обратного вызова не будет, и событие не будет отправлено снова после регистрации обратного вызова.

Мы можем легко использовать process.nextTick Чтобы решить эту проблему:

Это будет запущено, когда код основного процесса будет выполнен, и программа простаивает Event Loop Процесс, чтобы найти, если есть какие-либо микро задачи перед отправкой init Событие.

Источник

Портал знаний