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

программист JavaScript

оригинал:Джейк Арчибальд.com/2015/tasks-…

Переводчик: Front-end Xiaozhi

Ставь лайк и смотри, поиск в WeChat【Переезд в мир】Обратите внимание на этого человека, который не имеет большого фабричного прошлого, но имеет восходящий и позитивный настрой. эта статьяGitHub GitHub.com/QQ449245884…Он был включен, статьи были классифицированы, и многие мои документы и учебные материалы были систематизированы.

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

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

console.log('script start');

setTimeout(function() {
  console.log('setTimeout');
}, 0);

Promise.resolve().then(function() {
  console.log('promise1');
}).then(function() {
  console.log('promise2');
});

console.log('script end');

Каков порядок печати консоли?

Отвечать

Правильный ответ:script start, script end, promise1, promise2, setTimeout, но результаты противоречивы из-за поддержки различных браузеров.

Microsoft Edge, Firefox 40, iOS Safari и Desktop Safari 8.0.8 Печатьpromise1а такжеpromise2До сначала будет печатьsetTimeout- Похоже, производители браузеров различаются. Это действительно странно, потому что Firefox 39 и Safari 8.0.7 всегда корректны.

почему это

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

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

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

TasksВ источнике задачи этот браузер может войти в домен JavaScript/DOM изнутри и убедиться, что эти операции в порядке. Во время выполнения ЗАДАЧ браузер может обновить рендеринг. Это также относится к задаче от щелчка мыши до обратного вызова события.

setTimeoutзадержать на заданное время, а затем запланировать новую задачу для ее обратного вызова. поэтомуsetTimeoutпечатьscript endпосле печати, потому что печатьscript endявляется частью первой задачи, аsetTimeoutв отдельной задаче.

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

Пока в середине выполнения и в конце каждой задачи нет другого JavaScript, очередь микрозадач обрабатывается после обратного вызова. Любые другие микрозадачи, поставленные в очередь во время микрозадачи, добавляются в конец очереди и обрабатываются. Микрозадачи включают обратные вызовы MutationObserver. Например, в приведенном выше примереpromiseизcallback.

ОдинsettledОбещание государства или сталоsettledсостояние (асинхронный запрос урегулирован) обещание, оно будет немедленноcallback(then)Поместите его в очередь микрозадач.

Это гарантируетpromiseОбратные вызовы являются асинхронными, даже еслиpromiseсталsettledусловие. Поэтомуsettledизpromiseпередача.then(yey,nay)немедленно добавит микрозадачу в очередь микрозадач.

поэтомуpromise1а такжеpromise2Будет вscript endпосле печати, потому что работающий в данный момент скрипт должен завершиться перед обработкой микрозадачи.promise1а такжеpromise2существуетsetTimeoutпечатать раньше, потому что микрозадачи всегда выполняются до следующей.

ОК, запустите его шаг за шагом:

В чем будет разница между браузерами?

Порядок, в котором печатают некоторые браузеры,script start, script end, setTimeout, promise1, promise2. они вsetTimeoutбежать заpromiseПерезвоните. Звонят скорее всегоpromiseОбратный вызов выполняется как часть новой задачи, а не как микрозадача.

Это также понятно, потому чтоpromiseИз ECMAScript вместо HTML. ECMAScript имеет "Операция"концепция похожа на микрозадачи, но отношения не ясны за пределами расплывчатых обсуждений в списке рассылки. Однако общий консенсус заключается в том, чтоpromiseДолжен быть частью очереди микрозадач и не зря.

БудуpromiseОбработка как задачи может привести к проблемам с производительностью, поскольку обратные вызовы не обязательно нужно откладывать для вещей, связанных с задачей (например, рендеринга). Это также вызывает недетерминизм из-за взаимодействия с другими источниками задач и может нарушить взаимодействие с другими API, подробнее об этом позже.

Вот обратная связь по краюэто неправильноpromisesкак задача. WebKit nightly сделал это правильно, поэтому я думаю, что Safari в конечном итоге исправит это, и в Firefox 43, похоже, это есть.

Как узнать, использует ли что-то задачи или микрозадачи

Попробовать это - один из способов увидеть относительноpromiseа такжеsetTimeoutКак печатать, хотя это зависит от правильной реализации.

Один из способов — посмотреть спецификацию: поставить задачу в очередь:step 14 of setTimeout

Добавьте микрозадачу в очередь:step 5 of queuing a mutation record

Как упоминалось выше, ECMAScript называет микрозадачи заданиями: вызовите EnqueueJob, чтобы поставить микрозадачу в очередь:step 8.a of PerformPromiseThen

Дагуай уровня босса

Вот кусок html-кода:

<div class="outer">
  <div class="inner"></div>
</div>

Дайте код JS ниже, если вы нажметеdiv.innerЧто вы будете печатать?

// Let's get hold of those elements
var outer = document.querySelector('.outer');
var inner = document.querySelector('.inner');

// Let's listen for attribute changes on the
// outer element
new MutationObserver(function() {
  console.log('mutate');
}).observe(outer, {
  attributes: true
});

// Here's a click listener…
function onClick() {
  console.log('click');

  setTimeout(function() {
    console.log('timeout');
  }, 0);

  Promise.resolve().then(function() {
    console.log('promise');
  });

  outer.setAttribute('data-random', Math.random());
}

// …which we'll attach to both elements
inner.addEventListener('click', onClick);
outer.addEventListener('click', onClick);

Попробуйте, прежде чем искать ответ

попробуй

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

кто прав?

Расписание'click«Событие — это задача. Наблюдатель мутаций и обратные вызовы обещаний перечислены как микрозадачи.setTimeoutОбратный вызов указан как задача. Таким образом, процесс запуска выглядит следующим образом:

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

Что не так с браузером?

Для мутационных обратных вызовов как Firefox, так и Safari правильно завершают события щелчка внутренней и внешней областей, очищая очередь микрозадач, ноpromisesОбработка очереди выглядит иchromeРазные. Это несколько простительно, так как связь между заданиями и микрозадачами не ясна, но я все же ожидаю обработки между обратными вызовами событий.Firefox ticket. Safari ticket.

Для Edge мы видели это неправильноpromisesВ качестве задачи он не устанавливает очередь MicroTask между Click Callbacks, но после того, как выполняются обратные вызовы кликов, поэтому в общей сложности только одинmutateв двоемclickраспечатать потом.

Улучшение босса 1-го уровня для битвы с монстром

Все еще используя приведенный выше пример, что, если мы запустим следующий код:

inner.click();

Как и прежде, он сработаетclickсобытие, но на этот раз оно вызывается через JS.

попробуй

Вот как работает каждый браузер:

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

Почему это не так?

Это должно выглядеть так:

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

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

Суммировать

Задачи выполняются последовательно, и браузер может отображать между ними:

Микрозадачи выполняются последовательно, и выполняются:

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

  • в конце каждого задания.

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

общаться с

Статья постоянно обновляется каждую неделю. Вы можете выполнить поиск «Big Move to the World» в WeChat, чтобы прочитать и обновить ее как можно скорее (на одну или две статьи раньше, чем в блоге). Эта статья находится на GitHub.GitHub.com/QQ449245884…Он был включен, и многие мои документы были разобраны. Добро пожаловать в Звезду и совершенство. Вы можете обратиться в тестовый центр для ознакомления во время собеседования. Кроме того, обратите внимание на паблик-аккаунт и ответьте в фоновом режиме.Благосостояние, вы можете увидеть преимущества, вы знаете.