Вызовите setTimeout без задержки - PullRequest
36 голосов
/ 31 января 2012

Довольно часто в библиотеках JavaScript код выглядит так:

setTimeout(function() {
    ...
}, 0);

Я хотел бы знать, зачем использовать такой код-обертку.

Ответы [ 5 ]

57 голосов
/ 31 января 2012

Очень упрощенно:

Браузеры являются однопоточными, и этот однопоточный поток (поток пользовательского интерфейса) используется совместно механизмом рендеринга и механизмом js.

Если то, что вы хотите сделать, занимает много времени (мы говорим здесь циклы, но все же), это может остановить (приостановить) рендеринг (поток и рисование).

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

Используя setTimeout, вы создаете новую задачу в корзине после задержки и позволяете потоку справиться с ней, как только она станет доступной для дальнейшей работы.

История:

После задержки 0 мс создайте новое задание функции и положить его в ведро. В этот момент поток пользовательского интерфейса занят делать что-то еще, и есть другие задачи в ведре уже. Через 6 мс поток становится доступным и получает задачу перед из твоих, хорошо, ты следующий. Но что? Это была одна огромная вещь! Она имеет был как раньше (30мс) !!

Наконец-то, теперь тема завершена, приходит и получает задача.

У большинства браузеров минимальная задержка больше 0, поэтому установка 0 в качестве задержки означает: поместите эту задачу в корзину как можно скорее. Но указание UA положить его в ведро КАК МОЖНО СКОРЕЕ, это не гарантия, что он будет выполнен в тот момент. Ведро похоже на почтовое отделение, может быть, есть длинная очередь других задач. Почтовые отделения также являются однопоточными, и только один человек помогает выполнять все задачи ... извините клиентов за их задачи. Ваша задача должна встать в очередь, как и все остальные.

Если браузер не реализует свой собственный тикер, он использует циклы тиков ОС. Старые браузеры имели минимальные задержки между 10-15 мс. HTML5 указывает , что если задержка меньше 4 мс, UA должен увеличить ее до 4 мс. Говорят, что это согласовано во всех браузерах, выпущенных в 2010 году и далее .

См. Как работают таймеры JavaScript от John Resig для более подробной информации.

Редактировать: Также см. Какого черта цикл событий в любом случае? от Филиппа Робертса из JSConf EU 2014. Это обязательный просмотр для всех, кто касается кода интерфейса.

9 голосов
/ 31 января 2012

Есть несколько причин, по которым вы могли бы сделать это

  • Существует действие, которое вы не хотите запускать немедленно, но хотите выполнить в ближайшем будущем.
  • Вы хотите разрешить запуск других ранее зарегистрированных обработчиков из setTimeout или setInterval
6 голосов
/ 23 января 2013

Помимо предыдущих ответов, я хотел бы добавить еще один полезный сценарий, о котором я могу подумать: «убежать» из блока try-catch.Задержка setTimeout из блока try-catch будет выполняться вне блока, а любое исключение будет распространяться в глобальной области видимости.

Возможно, лучший пример сценария: в сегодняшнем JavaScript, с более распространенным использованием такназываемый Deferreds / Promises для асинхронных обратных вызовов, которые вы (часто) фактически выполняете внутри try-catch.Отложенные / обещания обертывают обратный вызов в try-catch, чтобы иметь возможность обнаруживать и распространять исключение как ошибку в асинхронной цепочке.Это все хорошо для функций, которые должны быть в цепочке, но рано или поздно вы «сделали» (т.е. извлекли весь ваш ajax) и хотите запустить простой неасинхронный код, где вы не хотите, чтобы исключения были »скрытоAFAIK Dojo, Kris Kowal's Q, MochiKit и Google Closure lib используют упаковку try-catch (но не jQuery).

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

3 голосов
/ 31 января 2012

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

Пример:

function callMe()
{
   for(var i = 0; i < 100000; i++)
     {
       document.title = i;
     }
} 

var x = 10;
setTimeout(callMe, 0);

var el = document.getElementById('test-id');
el.innerHTML = 'Im done before callMe method';

Вот почему я использую его.

1 голос
/ 31 января 2012

Чтобы разрешить выполнение любых ранее установленных таймаутов.

...