Чрезвычайно важно понимать, как работают таймеры JavaScript. Часто их поведение не соответствует нашему интуитивному пониманию многопоточности, и это связано с тем, что на самом деле они выполняются в одном потоке.
Давайте рассмотрим четыре функции, с помощью которых мы можем управлять таймерами:
- вар идентификатор = setTimeout (fn, задержка); - Создает простой таймер, который будет вызывать заданную функцию после заданной задержки.
Функция возвращает уникальный идентификатор, с помощью которого таймер можно приостановить.
- вар идентификатор = setInterval (fn, задержка); — Аналогично setTimeout, но непрерывно вызывает функцию через заданный интервал (пока не будет остановлена).
- clearInterval(id);,clearTimeout(id); — Принимает идентификатор таймера (возвращенный одной из описанных выше функций) и останавливает выполнение обратного вызова.
Начнем с того, что браузер выполняет все асинхронные события JavaScript в одном потоке (например, щелчки мыши или таймеры) и только в тот момент, когда наступает очередь этого события.
Лучше всего это демонстрирует следующая диаграмма:
Этот рисунок содержит довольно много информации, но его понимание даст вам более глубокое понимание того, как работает асинхронность JavaScript. На этой диаграмме показано время по вертикали в миллисекундах, синие блоки показывают блоки кода JavaScript, которые были выполнены.
Например, первый блок выполняется в среднем за 18мс, щелчок мышью блокирует выполнение примерно за 11мс и т.д. JavaScript может выполнять только один фрагмент кода (из-за однопоточного характера выполнения), каждый из которых блокирует выполнение других асинхронных событий.
Это означает, что когда происходит асинхронное событие (например, щелчок мыши, вызов таймера или завершение запроса XMLHttp), оно добавляется в очередь и выполняется позже (конечно, реализация зависит от браузера, но давайте согласимся назовем это «очередью»).
Для начала представим, что внутри блока JavaScript запускаются два таймера: setTimeout с задержкой 10 мс и setInterval с такой же задержкой.
В зависимости от того, когда запустится таймер, он сработает в тот момент, когда мы еще не завершили первый блок кода.
Однако обратите внимание, что он не срабатывает немедленно (это невозможно из-за однопоточной обработки).
Вместо этого отложенная функция ставится в очередь и выполняется в следующий доступный момент. Также во время выполнения первого блока JavaScript происходит щелчок мышью.
Обработчик этого асинхронного события (а оно асинхронное, потому что мы не можем его предсказать) не может быть выполнен непосредственно в этот момент, поэтому он тоже попадает в очередь, как и таймер.
После выполнения первого блока кода JavaScript браузер задает вопрос: «Что ожидает выполненияЭ» В этом случае обработчик щелчка мыши и таймер находятся в состоянии ожидания.
Браузер выбирает один из них (обработчик кликов) и выполняет его.
Таймер будет ждать следующего доступного отрезка времени в очереди выполнения.
Обратите внимание, что во время выполнения обработчика щелчка мыши срабатывает первый интервал обратного вызова.
Как и обратный вызов таймера, он будет поставлен в очередь.
Однако обратите внимание, что когда интервал сработает снова (во время обратного вызова таймера), он будет удален из очереди.
Если бы все интервальные обратные вызовы были поставлены в очередь во время выполнения большого фрагмента кода, это привело бы к множеству функций, ожидающих вызова, без периодов задержки между завершением их выполнения.
Вместо этого браузеры склонны ждать, пока в очереди не останется функций, прежде чем добавлять в очередь еще одну.
Таким образом, мы можем наблюдать случай, когда третье срабатывание интервала-коллбека совпадает с моментом его уже выполнения.
Это иллюстрирует важный момент: интервалам не важно, что выполняется в данный момент, они будут добавлены в очередь независимо от периода задержки между выполнениями.
Наконец, после завершения второго интервального обратного вызова мы увидим, что движку JavaScript больше нечего выполнять.
Это означает, что браузер снова ждет появления новых асинхронных событий.
Это произойдет на отметке 50 мс, когда интервальный обратный вызов снова заработает. В этот момент его уже нечем заблокировать, поэтому он заработает сразу.
Давайте рассмотрим пример, который хорошо иллюстрирует разницу между setTimeout и setInterval.
Эти два варианта на первый взгляд равнозначны, но на самом деле это не так.setTimeout(function(){ /* Some long block of code. */ setTimeout(arguments.callee, 10); }, 10); setInterval(function(){ /* Some long block of code. */ }, 10);
Код, использующий setTimeout, всегда будет иметь задержку не менее 10 мс после предыдущего вызова (она может быть больше, но не меньше), тогда как код, использующий setInterval, будет вызываться каждые 10 мс, независимо от того, когда произошел предыдущий вызов.
Подведем итог всему сказанному выше: - Движки JavaScript используют однопоточную среду, преобразуя асинхронные события в очередь, ожидающую выполнения, — Функции setTimeout и setInterval в асинхронном коде выполняются принципиально по-разному, - Если таймер не может быть выполнен в данный момент, он будет отложен до следующей точки выполнения (которая будет дольше желаемой задержки) — Интервалы (setInterval) могут выполняться один за другим без задержки, если их выполнение занимает больше времени, чем указанная задержка.
Все это чрезвычайно важная информация для развития.
Знание того, как работает движок JavaScript, особенно с большим количеством асинхронных событий (что часто случается), закладывает отличную основу для создания продвинутых приложений.
Теги: #JavaScript #john resig #john resig #timer #interval #JavaScript engine #threading #JavaScript
-
Эпоха Рутм
19 Oct, 24 -
Bbc Удалит 172 Своих Сайта
19 Oct, 24 -
Komodo Edit: Сборка Пакета Deb
19 Oct, 24 -
Платформа Autodafe — Работа С Моделями
19 Oct, 24