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