2013-09-20 3 views
3

Может кто-нибудь объяснить мне, почему при запуске программы под оповещениями я получаю всегда «aaa», а затем «bbb»? Я ожидал бы последовать шаги:Javascript событий огонь в неправильном порядке?

  1. запуске программы

  2. прогонов программы и получает SetTimeout линии. Он устанавливает таймер, и этот таймер будет запущен через 5 секунд с этого момента (не когда программа закончится). В очереди событий ничего не осталось

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

  4. Прошло 5 секунд, возможно, цикл по-прежнему работает, и таймер мы устанавливаем с помощью огней setTimeout. Это приводит к тому, что таймеры обращаются к «aaa()» в очередь событий в качестве второго обратного вызова события, которое должно выполняться прямо за событием клика, которое уже находится в очереди.

  5. Так что, когда программа заканчивается, я ожидаю, что она сначала предупредит «bbb», когда это событие было запущено первым (нажмите до 5 секунд), а затем «aaa» (пожар события setTimeout), но я всегда получаю «aaa» », а затем« bbb », почему?

Не является ли очередь событий Сначала в FIFO (сначала в первом случае)? Это зависит от браузера? Почему это происходит?

function aaa() { 
    alert("aaa"); 
} 

setTimeout(aaa, 5000); 

document.onclick = function() { 
    alert("bbb"); 
} 

for (i = 0; i < 1000000; i++) { 
    console.log(i); 
} 

JSFiddle здесь http://jsfiddle.net/sQvYG/1/

EDIT: Если я что-то (возможно, очень обидное) отсутствует, похоже, приведенный выше код делает очень похожую вещь, что объясняет Джон Resigs блог в деталях на http://ejohn.org/blog/how-javascript-timers-work/. Точно так же, начинается таймер, щелчок происходит и добавляется в очередь. Таймер запускается и добавляется в очередь. Выполнение завершается, и обработчик клика выполняется первым, и это то, чего я ожидал бы. Кто-нибудь может пролить свет на то, почему это не происходит в правильном порядке для меня?

EDIT: благодаря bfavaretto, который доставил принятый ответ и был достаточно любезен, чтобы подробно объяснить очереди, я понял, что нет никого, кроме нескольких «очередей задач», где размещаются события. Таким образом, 5 шагов я первоначально описанные выше, на самом деле это:

ШАГ 1. (это то же самое) я начинаю программу

ШАГ 2. (это те же) запускается программа и получает SetTimeout линии , Он устанавливает таймер, и этот таймер будет запущен через 5 секунд с этого момента (не когда программа закончится). Пока еще ничего не помещено в очередь событий.

Также я считаю, на данный момент ничего не помещается в очередь заданий только пока, как я нашел на MOZILLA Dev странице https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/EventLoop это:

«Вызов SetTimeout добавит сообщение в очередь после того, как время, прошедшее в качестве второго аргумента «.

Я также проверил это в Chrome и кажется, что обратный вызов добавляется в очередь задач, когда его фактически запускают.

ШАГ 3.затем, до передачи 5 секунд, я нажимаю на документ. Поскольку программа занята и все еще работает в цикле, она будет включать обратный вызов для этого события click в одной из очередей задач, которые браузер предоставляет для такого типа событий (щелчок мыши, очередь задач ui). Это единственное событие в этой очереди задач на данный момент. Другие очереди задач, например. для тайм-аутов в настоящий момент пуст.

ШАГ 4. 5 секунд прошло, петля, вероятно, все еще работает и таймер, который мы установили с помощью setTimeout пожаров. Это ставит таймеры callback «aaa()» в очередь задач, предоставляемую браузером специально для такого рода событий (таймаутов). Итак, теперь у нас есть одна очередь задач, связанная с пользовательскими взаимодействиями, и у нас есть один обратный вызов в ожидании (обратный вызов из нашего клика ранее в пункте 3). И у нас есть еще одна очередь задач, которая имеет дело с тайм-аутами, и у нас есть один обратный вызов и там. Это тот, у кого есть метод aaa(), который был запущен только через 5 секунд.

ШАГ 5. Итак, когда заканчивается программа, у нас есть две очереди задач с одним обратным вызовом. Теперь, даже если клик поместился в очередь задач сначала , нет никакой гарантии, что эта очередь задач (которая является взаимодействием пользователя или что-то, что касается щелчков мыши) будет обработана в первую очередь. Это полностью зависит от реализации браузера, и это то, что давало мне головные боли, пока бфаваретто не объяснил это в своем ответе.

Итак, в этом примере я упомянул только о двух очередях задач, однако браузер может иметь другие. Когда программа работает и занята, каждый тип события помещается в очередь задач для своего типа. После завершения программы браузер решает, какие очереди задач обрабатывать, отбирает обратный вызов оттуда, выполняет, как только закончил, захватывает что-то из тех же или других задач и так далее. Таким образом, на самом деле нет способа узнать, какая очередность будет обработана следующим образом. Однако задачи в каждой конкретной очереди всегда обрабатываются.

Ooff .. это, кажется, имеет смысл сейчас, статья Резига дала мне общее представление о том, как дела обстоят, но, как оказалось, это гораздо больше, и, надеюсь, наконец-то я понял это правильно.

+0

Что это для for (i = 0; i <1000000; i ++) { console.log (i); } без него скрипт отлично работает. –

+0

@YussufS: Это свидетельствует о непонимании событий JS, что это такое. : P Ничего не происходит UIwise, пока скрипт не вернется. – cHao

+0

Мой первоначальный ответ не отвечает на ваш обновленный вопрос. Я только что добавил новый, отдельный ответ об этом. Я решил не удалять свой предыдущий ответ, так как я считаю, что он разъясняет некоторые недоразумения из первой части вашего вопроса. – bfavaretto

ответ

3

Точное поведение зависит от пользователя-агента.В Chrome ваша скрипка сначала предупреждает «aaa», но в Firefox она сначала предупреждает «bbb» (предполагая, что в обоих случаях клик мыши выполняется, когда пользовательский интерфейс блокируется циклом for и что таймер истекает до того, как этот цикл законченный).

Я считаю, что статья Resig является результатом его наблюдения за тем, как веб-браузеры (или, может быть, определенный веб-браузер) вел себя в то время, когда он был написан (еще в 2008 году). Это не касается деталей относительно поведения, может быть, потому, что в то время не было четкой спецификации (я не уверен в этом). Возьмем следующий абзац, который является ключевым для вашего вопроса (выделено мной):

После того, как начальный блок JavaScript заканчивается, браузер сразу же задает вопрос: что ждет выполнения? В этом случае ожидают обработчик щелчка мыши и обратный вызов таймера. Затем браузер выбирает один (обратный вызов щелчка мыши) и выполняет его немедленно. Таймер будет ждать следующего возможного времени для выполнения.

В статье не объясняется, почему первый щелчок мыши выбран. Возможно, Resig заявила, что, основываясь на том, как работает Firefox. Моя интуиция, с другой стороны, ближе к тому, что делает Chrome: сначала обратный вызов таймера был добавлен в очередь, поэтому он запускается первым. Некоторые из высказываний Ресига расплывчаты, некоторые из них неточны; он говорит, что таймеры и интервалы «огонь» и «выполнение» не определяют, что он подразумевает под этими терминами (здесь, например: «когда обработчик щелчка мыши выполняет первый обратный вызов интервала»; «выполнить», похоже, имеет два разных значения , т. е. выполнение синхронного кода и истечение тайм-аута таймера).

EDIT
Я только что проверил Resig's book. Он включил в книгу пересмотренную и расширенную версию этой статьи, в которой более понятным образом рассматриваются понятия и термины. Он также представил дополнительные предупреждения о том, что некоторые виды поведения зависят от браузера. Однако он также представил что-то (вероятно, ошибку), что все это путает; на сегодняшний день (2013-09-20), в этом нет ничего в errata.

Благодаря вашему вопросу я теперь понимаю, что; Я думал, что наконец понял внутреннюю работу таймеров и цикл событий, когда я читал статью Резига в первые несколько раз. Но пришло время искать более точный ответ, поэтому давайте обратимся к спецификации HTML5 и посмотрим, что он говорит.

Я предлагаю вам взглянуть на принимая timers section, но ключ находится на участке на event loop and queued tasks:

Цикл событие имеет одну или несколько очередей задач. Очередь задач представляет собой упорядоченный список задач, которые могут быть:

События
Асинхронный диспетчерская объект Event на конкретном объекте EventTarget задача.

Примечание: не все события отправляются с использованием очереди задач, многие из них отправляются синхронно во время других задач.

Синтаксический
HTML-анализатор tokenizing один или несколько байт, а затем обрабатывать любые полученные маркеры, как правило, является задачей.

Callbacks
Вызов обратного вызова асинхронно задача.

Использование ресурса
Когда алгоритм выбирает ресурс, если выборка происходит асинхронно, то обработку ресурса, как только некоторые или все из ресурса доступна задача.

Реагируя манипуляции DOM
Некоторые элементы имеют задачи, которые вызывают в ответ на манипуляции DOM, например, когда этот элемент вставлен в документ.

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

Например, пользовательский агент может иметь одну очередь задач для событий мыши и ключа (источник задачи взаимодействия с пользователем), а другой для всего остального. Затем пользовательский агент может отдать предпочтение клавиатурным и мышечным событиям по сравнению с другими задачами три четверти времени, поддерживая взаимодействие интерфейса, но не голодая в других очередях задач, и никогда не обрабатывать события из одного источника задачи из строя.

Итак, мы имеем задачи, которые принадлежат к одному из многих очередей задач (или источников задачи). Обратные вызовы по таймеру добавляются в одну очередь, а события кликов добавляются в другую очередь. Каждая очередь упорядочена, но пользовательские агенты могут решать, как приоритеты очередей относительно друг друга. Обратите внимание на последний абзац. Кажется, Firefox отдает предпочтение очереди событий пользовательского интерфейса. Chrome, похоже, отдает предпочтение другому источнику задания, в котором заданы функции обратного вызова таймера.

+0

Это отличный ответ, и все, кажется, имеет смысл для меня сейчас. Я обновлю свой вопрос с помощью EDIT и информации, которую вы объяснили в своем ответе здесь, надеюсь, кто-то другой найдет это полезным. Еще раз спасибо, я очень ценю вашу помощь! – spirytus

4

программа запускается и переходит в режим setTimeout. Он устанавливает таймер, и этот таймер будет запущен через 5 секунд с этого момента (не когда программа закончится). В очереди событий ничего не осталось

Обратный вызов таймера немедленно добавляется в очередь. Он будет отложен, если цикл события будет отмечен до передачи 5000 мс. Это означает, что таймер займет больше 5 секунд, если цикл for еще не закончен.

затем, до передачи 5 секунд, я нажимаю документ.

Это недостаток. Если цикл все еще запущен, пользовательский интерфейс блокируется, и ваш клик не имеет возможности зарегистрироваться до первого предупреждения («aaa»).

5 секунд прошло, цикл, вероятно, все еще работает и таймер, который мы установили с помощью огней setTimeout.

Недостатки. Мы все еще находимся на первом тике цикла событий до тех пор, пока цикл for не будет завершен. Вызов таймера не может быть запущен до тех пор.

+0

wow, поэтому, когда происходит клик, событие еще не добавлено в очередь? Будет ли добавлено исполнение AFTER JS? Когда таймер setTimeout срабатывает, однако, через 5 секунд, то даже если JS-скрипт все еще работает внутри цикла, это событие будет добавлено сразу в очередь? – spirytus

+0

Я не уверен, что щелчок переходит в очередь до или после срабатывания обратного вызова таймера. Я пытаюсь сказать, что весь синхронный код должен быть закончен до того, как цикл цикла будет отмечен. События пользовательского интерфейса являются асинхронными, как таймеры. – bfavaretto

+0

спасибо за это bfavaretto! Однако я борюсь с тем, как он вписывается в объяснение Резига. В диаграмме 1. На Рис. 1. запускается программа, 2. (программа все еще работает) установлен таймер 3. (программа все еще работает) сначала происходит щелчок, 4. (программа все еще работает) срабатывает таймер, 5. заканчивается программа, 6. обратный вызов клика выполняется так, как это произошло в (3) до срабатывания таймера (4). извините, если я больше не имею смысла здесь:/и спасибо за вашу помощь до сих пор – spirytus

Смежные вопросы