renovations

This commit is contained in:
Ilya Kantor 2015-02-07 12:34:26 +03:00
parent 66e2f0919d
commit 25fc5d8650
19 changed files with 268 additions and 88 deletions

View file

@ -1,15 +1,15 @@
# Порядок обработки событий
События могут возникать не только по очереди, но и пачкой, по многу сразу. Возможно и такое, что во время обработки одного события возникают другие.
События могут возникать не только по очереди, но и "пачкой" по много сразу. Возможно и такое, что во время обработки одного события возникают другие, например пока выполнялся код для `onclick` -- посетитель провёл мышкой, а это уже `mousemove`.
Здесь и далее, очень важно понимать, как браузер обычно работает с событиями и важные исключения из этого правила. Это мы и разберём в этой главе.
Здесь мы разберём, как браузер обычно работает с одновременно возникающими событиями и какие есть исключения из общего правила.
[cut]
## Главный поток
В каждом окне выполняется только один *главный* поток, который занимается выполнением JavaScript, отрисовкой и работой с DOM.
Он выполняет команды последовательно и блокируется при выводе модальных окон, таких как `alert`.
Он выполняет команды последовательно, может делать только одно дело одновременно и блокируется при выводе модальных окон, таких как `alert`.
[smart header="Дополнительные потоки тоже есть"]
@ -21,9 +21,9 @@
[smart header="Web Workers"]
Существует спецификация <a href="http://www.w3.org/TR/workers/">Web Workers</a>, которая позволяет запускать дополнительные JavaScript-процессы(workers).
Они могут обмениваться сообщениями с главным процессом, но их переменные полностью независимы.
Они могут обмениваться сообщениями с главным процессом, но у них свои переменные, и работают они также сами по себе.
В частности, дополнительные процессы не имеют доступа к DOM, поэтому они полезны, преимущественно, при вычислениях, чтобы загрузить несколько ядер/процессоров одновременно.
Такие дополнительные процессы не имеют доступа к DOM, поэтому они полезны, преимущественно, при вычислениях, чтобы загрузить несколько ядер/процессоров одновременно.
[/smart]
## Очередь событий
@ -36,18 +36,17 @@
**Когда происходит событие, оно попадает в очередь.**
Внутри браузера существует главный внутренний цикл, который проверяет очередь и обрабатывает события, запускает соответствующие обработчики и т.п.
Внутри браузера непрерывно работает "главный внутренний цикл", который следит за состоянием очереди и обрабатывает события, запускает соответствующие обработчики и т.п.
**Иногда события добавляются в очередь сразу пачкой.**
Например, при клике на элементе генерируется несколько событий:
<ol>
<li>Сначала `mousedown` -- нажата кнопка мыши.</li>
<li>Затем `mouseup` -- кнопка мыши отпущена.</li>
<li>Так как это было над одним элементом, то дополнительно генерируется `click`</li>
<li>Затем `mouseup` -- кнопка мыши отпущена и, так как это было над одним элементом, то дополнительно генерируется `click` (два события сразу).</li>
</ol>
[online]
В действии:
```html
@ -61,14 +60,17 @@
area.onclick = function(e) { this.value += "click\n"; this.scrollTop = 1e9; };
</script>
```
[/online]
Таким образом, при нажатии кнопки мыши в очередь попадёт событие `mousedown`, а при отпускании -- сразу два события: `mouseup` и `click`. Браузер сначала обработает первое, а потом -- второе.
Таким образом, при нажатии кнопки мыши в очередь попадёт событие `mousedown`, а при отпускании -- сразу два события: `mouseup` и `click`. Браузер обработает их строго одно за другим: `mousedown` -> `mouseup` -> `click`.
**При этом каждое событие из очереди обрабатывается полностью отдельно от других.**
При этом каждое событие из очереди обрабатывается полностью отдельно от других.
## Вложенные (синхронные) события
В тех случаях, когда событие инициируется не посетителем, а кодом, то оно, как правило, обрабатывается синхронно, то есть прямо сейчас.
Обычно возникающие события "становятся в очередь".
Но в тех случаях, когда событие инициируется не посетителем, а кодом, то оно, как правило, обрабатывается синхронно, то есть прямо сейчас.
Рассмотрим в качестве примера событие `onfocus`.
@ -96,9 +98,9 @@
</script>
```
В главе [](/focus-blur) мы познакомимся с этим событием подробнее, а пока -- нажмите на кнопку в примере ниже. При этом обработчик `onclick` вызовет метод `focus()` на текстовом поле `text`.
В главе [](/focus-blur) мы познакомимся с этим событием подробнее, а пока -- нажмите на кнопку в примере ниже.
**Событие `onfocus`, инициированное вызовом `text.focus()`, будет обработано синхронно, прямо сейчас, до завершения `onclick`.**
При этом обработчик `onclick` вызовет метод `focus()` на текстовом поле `text`. Код обработчика `onfocus`, который при этом запустится, сработает синхронно, прямо сейчас, до завершения `onclick`.
```html
<!--+ autorun -->
@ -121,12 +123,13 @@
</script>
```
При клике на кнопке в примере выше будет видно, что управление вошло в `onclick`, затем перешло в `onfocus`, затем вышло из `onclick`.
При клике на кнопке в примере выше будет видно, что управление вошло в `onclick`, затем перешло в `onfocus`, затем вышло из `onclick`.
**Так ведут себя все браузеры, кроме IE.**
[warn header="Исключение в IE"]
Так ведут себя все браузеры, кроме IE.
В нём событие `onfocus` -- всегда асинхронное, так что будет сначала полностью обработан клик, а потом -- фокус. В остальных -- фокус вызовется посередине клика. Попробуйте кликнуть в IE и в другом браузере, чтобы увидеть разницу.
[/warn]
## Делаем события асинхронными через setTimeout(...,0)
@ -134,7 +137,7 @@
Можно добиться и этого.
Один вариант -- просто переместить строку `text.focus()` вниз кода обработчика.
Один вариант -- просто переместить строку `text.focus()` вниз кода обработчика `onclick`.
Если это неудобно, можно запланировать `text.focus()` чуть позже через `setTimeout(..., 0)`, вот так
@ -173,3 +176,6 @@
<li>Синхронными являются вложенные события, инициированные из кода.</li>
<li>Чтобы сделать событие гарантированно асинхронным, используется вызов через `setTimeout(func, 0)`.</li>
</ul>
Отложенный вызов через `setTimeout(func, 0)` используется не только в событиях, а вообще -- всегда, когда мы хотим, чтобы некая функция `func` сработала после того, как текущий скрипт завершится.