# Порядок обработки событий События могут возникать не только по очереди, но и "пачкой" по много сразу. Возможно и такое, что во время обработки одного события возникают другие, например пока выполнялся код для `onclick` -- посетитель нажал кнопку на клавиатуре (событие `keydown`). Здесь мы разберём, как браузер обычно работает с одновременно возникающими событиями и какие есть исключения из общего правила. [cut] ## Главный поток В каждом окне выполняется только один *главный* поток, который занимается выполнением JavaScript, отрисовкой и работой с DOM. Он выполняет команды последовательно, может делать только одно дело одновременно и блокируется при выводе модальных окон, таких как `alert`. ```smart header="Дополнительные потоки тоже есть" Есть и другие, служебные потоки, например, для сетевых коммуникаций. Поэтому скачивание файлов может продолжаться пока главный поток ждёт реакции на `alert`. Но управлять служебными потоками мы не можем. ``` ```smart header="Web Workers" Существует спецификация Web Workers, которая позволяет запускать дополнительные JavaScript-процессы(workers). Они могут обмениваться сообщениями с главным процессом, но у них свои переменные, и работают они также сами по себе. Такие дополнительные процессы не имеют доступа к DOM, поэтому они полезны, преимущественно, при вычислениях, чтобы загрузить несколько ядер/процессоров одновременно. ``` ## Очередь событий Произошло одновременно несколько событий или во время работы одного случилось другое -- как главному потоку обработать это? Если главный поток прямо сейчас занят, то он не может срочно выйти из середины одной функции и прыгнуть в другую. А потом третью. Отладка при этом могла бы превратиться в кошмар, потому что пришлось бы разбираться с совместным состоянием нескольких функций сразу. Поэтому используется альтернативный подход. **Когда происходит событие, оно попадает в очередь.** Внутри браузера непрерывно работает "главный внутренний цикл", который следит за состоянием очереди и обрабатывает события, запускает соответствующие обработчики и т.п. **Иногда события добавляются в очередь сразу пачкой.** Например, при клике на элементе генерируется несколько событий: 1. Сначала `mousedown` -- нажата кнопка мыши. 2. Затем `mouseup` -- кнопка мыши отпущена и, так как это было над одним элементом, то дополнительно генерируется `click` (два события сразу). ````online В действии: ```html autorun height=150 no-beautify ``` ```` Таким образом, при нажатии кнопки мыши в очередь попадёт событие `mousedown`, а при отпускании -- сразу два события: `mouseup` и `click`. Браузер обработает их строго одно за другим: `mousedown` -> `mouseup` -> `click`. При этом каждое событие из очереди обрабатывается полностью отдельно от других. ## Вложенные (синхронные) события Обычно возникающие события "становятся в очередь". Но в тех случаях, когда событие инициируется не посетителем, а кодом, то оно, как правило, обрабатывается синхронно, то есть прямо сейчас. Рассмотрим в качестве примера событие `onfocus`. ### Пример: событие onfocus Когда посетитель фокусируется на элементе, возникает событие `onfocus`. Обычно оно происходит, когда посетитель кликает на поле ввода, например: ```html run height=80 autorun

При фокусе на поле оно изменит значение.

``` Но ту же фокусировку можно вызвать и явно, вызовом метода `elem.focus()`: ```html run ``` В главе мы познакомимся с этим событием подробнее, а пока -- нажмите на кнопку в примере ниже. При этом обработчик `onclick` вызовет метод `focus()` на текстовом поле `text`. Код обработчика `onfocus`, который при этом запустится, сработает синхронно, прямо сейчас, до завершения `onclick`. ```html autorun height=80 no-beautify ``` При клике на кнопке в примере выше будет видно, что управление вошло в `onclick`, затем перешло в `onfocus`, затем вышло из `onclick`. ```warn header="Исключение в IE" Так ведут себя все браузеры, кроме IE. В нём событие `onfocus` -- всегда асинхронное, так что будет сначала полностью обработан клик, а потом -- фокус. В остальных -- фокус вызовется посередине клика. Попробуйте кликнуть в IE и в другом браузере, чтобы увидеть разницу. ``` ## Делаем события асинхронными через setTimeout(...,0) А что, если мы хотим, чтобы *сначала* закончилась обработка `onclick`, а потом уже произошла обработка `onfocus` и связанные с ней действия? Можно добиться и этого. Один вариант -- просто переместить строку `text.focus()` вниз кода обработчика `onclick`. Если это неудобно, можно запланировать `text.focus()` чуть позже через `setTimeout(..., 0)`, вот так ```html autorun height=80 ``` Такой вызов обеспечит фокусировку через минимальный "тик" таймера, по стандарту равный 4 мс. Обычно такая задержка не играет роли, а необходимую асинхронность мы получили. ## Итого - JavaScript выполняется в едином потоке. Современные браузеры позволяют порождать подпроцессы Web Workers, они выполняются параллельно и могут отправлять/принимать сообщения, но не имеют доступа к DOM. - Обычно события становятся в очередь и обрабатываются в порядке поступления, асинхронно, независимо друг от друга. - Синхронными являются вложенные события, инициированные из кода. - Чтобы сделать событие гарантированно асинхронным, используется вызов через `setTimeout(func, 0)`. Отложенный вызов через `setTimeout(func, 0)` используется не только в событиях, а вообще -- всегда, когда мы хотим, чтобы некая функция `func` сработала после того, как текущий скрипт завершится.