Почему setTimeout (fn, 0) иногда полезен?

Недавно я столкнулся с довольно неприятной ошибкой, в которой код загружал <select> динамически через JavaScript. Этот динамически загруженный <select> имел предварительно выбранное значение. В IE6 у нас уже был код для исправления выбранного <option>, потому что иногда значение <select> selectedIndex было бы не синхронизировано с выбранным атрибутом <option> index, как показано ниже:

field.selectedIndex = element.index;

Однако этот код не работал. Несмотря на то, что поле selectedIndex было установлено правильно, в результате будет выбран неправильный индекс. Однако, если я вставил инструкцию alert() в нужное время, будет выбран правильный вариант. Думая, что это может быть какая-то проблема времени, я пробовал что-то случайное, которое я видел в коде раньше:

var wrapFn = (function() {
    var myField = field;
    var myElement = element;

    return function() {
        myField.selectedIndex = myElement.index;
    }
})();
setTimeout(wrapFn, 0);

И это сработало!

У меня есть решение для моей проблемы, но мне неловко, что я точно не знаю, почему это устраняет мою проблему. Кто-нибудь имеет официальное объяснение? Какую проблему с браузером я избегаю, вызывая мою функцию "позже", используя setTimeout()?

Ответ 1

Это работает, потому что вы делаете совместную многозадачность.

Браузер должен делать сразу несколько вещей, и только одна из них - это выполнение JavaScript. Но одна из вещей, для которой очень часто используется JavaScript - это попросить браузер создать элемент отображения. Часто предполагается, что это делается синхронно (в частности, поскольку JavaScript не выполняется параллельно), но нет никакой гарантии, что это так, и JavaScript не имеет четко определенного механизма ожидания.

Решение состоит в том, чтобы "приостановить" выполнение JavaScript, чтобы потоки рендеринга наверстали упущенное. И это эффект, который делает setTimeout() с таймаутом 0. Это похоже на выход потока/процесса в C. Хотя кажется, что он говорит "запустите это немедленно", на самом деле он дает браузеру возможность завершить некоторые вещи, не связанные с JavaScript, которые ожидали завершения, прежде чем приступить к этому новому фрагменту JavaScript.,

(В действительности, setTimeout() помещает новый JavaScript в очередь в конце очереди выполнения. См. Комментарии для ссылок на более подробное объяснение.)

IE6 просто более склонен к этой ошибке, но я видел, что она встречается в старых версиях Mozilla и в Firefox.


Смотрите выступление Филиппа Робертса "Какого черта цикл событий?" для более подробного объяснения.

Ответ 2

Введение:

ВАЖНОЕ ЗАМЕЧАНИЕ: в то время как он больше всего одобрен и принят, принятый ответ @staticsan на самом деле является NOT CORRECT! - см. комментарий Дэвида Малдера для объяснения причин.

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

Как таковой, я публикую подробное описание того, что делает браузер, и как использовать setTimeout() помогает. Он выглядит длинным, но на самом деле очень простым и понятным - я просто очень подробно его описал.

ОБНОВЛЕНИЕ: Я сделал JSFiddle для показа - объясните ниже: http://jsfiddle.net/C2YBE/31/. Многие благодаря спасибо @ThangChung за помощь в его запуске.

UPDATE2: На всякий случай, когда веб-сайт JSFiddle умирает или удаляет код, я добавил код в этот ответ в самом конце.


ПОДРОБНОСТИ

Представьте веб-приложение с кнопкой "сделать что-нибудь" и результатом div.

Обработчик onClick для кнопки "сделать что-то" вызывает функцию "LongCalc()", которая выполняет 2 вещи:

  • Делает очень длинный расчет (например, занимает 3 минуты)

  • Распечатывает результаты вычисления в результате div.

Теперь ваши пользователи начнут тестировать это, нажмите кнопку "сделать что-то", и страница сидит там ничего не видя в течение 3 минут, они становятся беспокойными, снова нажимают кнопку, ждут 1 минуту, ничего не происходит, снова нажмите кнопку...

Проблема очевидна - вам нужен "статус" DIV, который показывает, что происходит. Посмотрим, как это работает.


Итак, вы добавляете DIV состояния (изначально пустым) и изменяете обработчик onClick (function LongCalc()), чтобы выполнить 4 вещи:

  • Задать статус "Вычисление... может занять 3 минуты" в статусе DIV

  • Делает очень длинный расчет (например, занимает 3 минуты)

  • Распечатывает результаты вычисления в результате div.

  • Залить статус "Расчет выполнен" в состояние DIV

И вы с радостью отнесите приложение к повторному тестированию.

Они возвращаются к вам очень злобно. И объясните, что когда они нажали кнопку, статус DIV никогда не обновлялся с статусом "Вычисление..."!!!


Вы почесываете голову, задаете вопрос о StackOverflow (или читаете документы или Google) и понимаете проблему:

Браузер размещает все свои задачи "TODO" (как задачи пользовательского интерфейса, так и команды JavaScript), возникающие в результате событий, в одиночную очередь. И, к сожалению, повторное рисование "статуса" DIV с новым значением "Расчет..." представляет собой отдельный TODO, который идет в конец очереди!

Здесь происходит разбивка событий во время теста пользователя, содержимое очереди после каждого события:

  • Очередь: [Empty]
  • Событие: нажмите кнопку. Очередь после события: [Execute OnClick handler(lines 1-4)]
  • Событие: выполните первую строку в обработчике OnClick (например, измените значение статуса DIV). Очередь после события: [Execute OnClick handler(lines 2-4), re-draw Status DIV with new "Calculating" value]. Обратите внимание, что, хотя изменения DOM происходят мгновенно, для повторного рисования соответствующего элемента DOM вам нужно новое событие, вызванное изменением DOM, которое было в конце очереди.
  • ПРОБЛЕМА!!! ПРОБЛЕМА!!! Детали, объясняемые ниже.
  • Событие: выполнить вторую строку в обработчике (расчет). Очередь после: [Execute OnClick handler(lines 3-4), re-draw Status DIV with "Calculating" value].
  • Событие: выполнить третью строку в обработчике (заполнить результат DIV). Очередь после: [Execute OnClick handler(line 4), re-draw Status DIV with "Calculating" value, re-draw result DIV with result].
  • Событие: выполнить 4-ю строку в обработчике (введите статус DIV с "DONE" ). Очередь: [Execute OnClick handler, re-draw Status DIV with "Calculating" value, re-draw result DIV with result; re-draw Status DIV with "DONE" value].
  • Событие: выполнить подразумеваемый return из onClick обработчик sub. Мы берем "Execute OnClick handler" из очереди и начинаем выполнение следующего элемента в очереди.
  • ПРИМЕЧАНИЕ. Поскольку мы уже закончили расчет, 3 минуты уже прошли для пользователя. Событие повторного розыгрыша еще не произошло.
  • Событие: повторите рисование статуса DIV с помощью значения "Расчет". Мы делаем повторную ничью и снимаем ее с очереди.
  • Событие: повторите рисование результата DIV с результатом результата. Мы делаем повторную ничью и снимаем ее с очереди.
  • Событие: повторите рисование статуса DIV со значением "Готово". Мы делаем повторную ничью и снимаем ее с очереди. Зрители с острыми глазами могут даже заметить "Status DIV с миганием" Расчет "для доли микросекунды - ПОСЛЕ РАСЧЕТА ЗАВЕРШЕНО

Итак, основная проблема заключается в том, что событие re-draw для "Status" DIV помещается в очередь в конце, ПОСЛЕ события "выполнить строку 2", которое занимает 3 минуты, поэтому фактическая повторная ничья не " t произойдет до ПОСЛЕ расчета.


На помощь приходит setTimeout(). Как это помогает? Потому что, вызывая длинный исполняемый код через setTimeout, вы фактически создаете 2 события: setTimeout сам выполнение и (из-за таймаута 0), отдельные записи очереди для исполняемого кода.

Итак, чтобы исправить вашу проблему, вы изменяете обработчик onClick как TWO-инструкции (в новой функции или только в блоке внутри onClick):

  • Задать статус "Вычисление... может занять 3 минуты" в статусе DIV

  • Выполнить setTimeout() с 0 таймаутом и вызовом функции LongCalc().

    LongCalc() функция почти такая же, как в прошлый раз, но, очевидно, не имеет статуса "Расчет..." DIV update в качестве первого шага; и вместо этого начинает расчет сразу.

Итак, как выглядит последовательность событий и очередь?

  • Очередь: [Empty]
  • Событие: нажмите кнопку. Очередь после события: [Execute OnClick handler(status update, setTimeout() call)]
  • Событие: выполните первую строку в обработчике OnClick (например, измените значение статуса DIV). Очередь после события: [Execute OnClick handler(which is a setTimeout call), re-draw Status DIV with new "Calculating" value].
  • Событие: выполнить вторую строку в обработчике (вызов setTimeout). Очередь после: [re-draw Status DIV with "Calculating" value]. В очереди нет ничего нового в течение еще 0 секунд.
  • Событие: Тревога от таймаута гаснет, через 0 секунд. Очередь после: [re-draw Status DIV with "Calculating" value, execute LongCalc (lines 1-3)].
  • Событие: перерисовать статус DIV с "Расчетным" значением. Очередь после: [execute LongCalc (lines 1-3)]. Обратите внимание, что это событие повторного розыгрыша может произойти, прежде чем сигнал тревоги погаснет, что также работает.
  • ...

Ура! Статус DIV только что обновился до "Расчет..." до начала расчета!!!



Ниже приведен пример кода из JSFiddle, иллюстрирующий эти примеры: http://jsfiddle.net/C2YBE/31/:

HTML-код:

<table border=1>
    <tr><td><button id='do'>Do long calc - bad status!</button></td>
        <td><div id='status'>Not Calculating yet.</div></td>
    </tr>
    <tr><td><button id='do_ok'>Do long calc - good status!</button></td>
        <td><div id='status_ok'>Not Calculating yet.</div></td>
    </tr>
</table>

Код JavaScript: (Выполнено в onDomReady и может потребоваться jQuery 1.9)

function long_running(status_div) {

    var result = 0;
    // Use 1000/700/300 limits in Chrome, 
    //    300/100/100 in IE8, 
    //    1000/500/200 in FireFox
    // I have no idea why identical runtimes fail on diff browsers.
    for (var i = 0; i < 1000; i++) {
        for (var j = 0; j < 700; j++) {
            for (var k = 0; k < 300; k++) {
                result = result + i + j + k;
            }
        }
    }
    $(status_div).text('calculation done');
}

// Assign events to buttons
$('#do').on('click', function () {
    $('#status').text('calculating....');
    long_running('#status');
});

$('#do_ok').on('click', function () {
    $('#status_ok').text('calculating....');
    // This works on IE8. Works in Chrome
    // Does NOT work in FireFox 25 with timeout =0 or =1
    // DOES work in FF if you change timeout from 0 to 500
    window.setTimeout(function (){ long_running('#status_ok') }, 0);
});
function long_running(status_div) {

    var result = 0;
    // Use 1000/700/300 limits in Chrome, 
    //    300/100/100 in IE8, 
    //    1000/500/200 in FireFox
    // I have no idea why identical runtimes fail on diff browsers.
    for (var i = 0; i < 1000; i++) {
        for (var j = 0; j < 700; j++) {
            for (var k = 0; k < 300; k++) {
                result = result + i + j + k;
            }
        }
    }
    $(status_div).text('calculation done');
}

// Assign events to buttons
$('#do').on('click', function () {
    $('#status').text('calculating....');
    long_running('#status');
});

$('#do_ok').on('click', function () {
    $('#status_ok').text('calculating....');
    // This works on IE8. Works in Chrome
    // Does NOT work in FireFox 25 with timeout =0 or =1
    // DOES work in FF if you change timeout from 0 to 500
    window.setTimeout(function (){ long_running('#status_ok') }, 0);
});

Ответ 3

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

Ответ 4

setTimeout() вы покупаете некоторое время, пока не будут загружены элементы DOM, даже если установлено значение 0.

Проверьте это: setTimeout

Ответ 5

В большинстве браузеров есть процесс, называемый основным потоком, который отвечает за выполнение некоторых задач JavaScript, обновление пользовательского интерфейса, например: рисование, перерисовка или перепланирование и т.д.

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

Когда обновления пользовательского интерфейса генерируются, когда основной поток занят, задачи добавляются в очередь сообщений.

setTimeout(fn, 0); добавьте этот fn в конец очереди, которая будет выполнена. Он планирует добавить задачу в очередь сообщений через заданный промежуток времени.

Ответ 6

Здесь есть противоречивые ответы, и без доказательств нет способа узнать, кому верить. Вот доказательство того, что @DVK верен, а @SalvadorDali ошибочен. Последний утверждает:

"И вот почему: невозможно установить setTimeout со временем задержка в 0 миллисекунд. Минимальное значение определяется браузера, и это не 0 миллисекунд. Исторически браузеры устанавливают это минимум до 10 миллисекунд, но спецификации HTML5 и современные браузеры установите его на 4 миллисекунды."

Минимальный тайм-аут 4 мс не имеет отношения к тому, что происходит. Что действительно происходит, так это то, что setTimeout выталкивает функцию обратного вызова в конец очереди выполнения. Если после setTimeout (callback, 0) у вас есть код блокировки, который занимает несколько секунд, то обратный вызов не будет выполняться в течение нескольких секунд, пока код блокировки не завершится. Попробуйте этот код:

function testSettimeout0 () {
    var startTime = new Date().getTime()
    console.log('setting timeout 0 callback at ' +sinceStart())
    setTimeout(function(){
        console.log('in timeout callback at ' +sinceStart())
    }, 0)
    console.log('starting blocking loop at ' +sinceStart())
    while (sinceStart() < 3000) {
        continue
    }
    console.log('blocking loop ended at ' +sinceStart())
    return // functions below
    function sinceStart () {
        return new Date().getTime() - startTime
    } // sinceStart
} // testSettimeout0

Выход:

setting timeout 0 callback at 0
starting blocking loop at 5
blocking loop ended at 3000
in timeout callback at 3033

Ответ 7

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

теперь отредактируйте, что в 2015 году я должен отметить, что там также requestAnimationFrame(), что не совсем то же самое, но достаточно близко к setTimeout(fn, 0), о котором стоит упомянуть.

Ответ 8

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

Итак, у вас есть две функции:

var f1 = function () {    
   setTimeout(function(){
      console.log("f1", "First function call...");
   }, 0);
};

var f2 = function () {
    console.log("f2", "Second call...");
};

а затем вызовите их в следующем порядке f1(); f2();, чтобы увидеть, что второй из них выполняется первым.

И вот почему: невозможно иметь setTimeout с задержкой в ​​0 миллисекунд. Минимальное значение определяется браузером, и оно не равно 0 миллисекундам. Исторически браузер устанавливает этот минимум в 10 миллисекунд, но спецификации HTML5 и современные браузеры устанавливают его в 4 миллисекунды,

Если уровень вложенности больше 5, а тайм-аут меньше 4, то увеличить таймаут до 4.

Также из mozilla:

Чтобы реализовать тайм-аут 0 мс в современном браузере, вы можете использовать window.postMessage(), как описано здесь.

P.S. информация берется после прочтения следующей статьи.

Ответ 9

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

Ответ 10

Оба из этих двух самых популярных ответов неверны. Посмотрите описание MDN для модели параллелизма и цикла обработки событий, и должно стать ясно, что происходит (этот ресурс MDN является настоящим украшением). И просто использование setTimeout может добавить неожиданные проблемы в ваш код в дополнение к "решению" этой маленькой проблемы.

Здесь на самом деле не то, что "браузер может быть еще не совсем готов, потому что параллелизм" или что-то на основе "каждая строка - это событие, которое добавляется в конец очереди".

Jsfiddle, предоставленный DVK, действительно иллюстрирует проблему, но его объяснение не является правильным.

В его коде происходит то, что он сначала присоединяет обработчик событий к событию click на кнопке #do.

Затем, когда вы фактически нажимаете кнопку, создается message ссылающееся на функцию обработчика событий, которая добавляется в message queue. Когда event loop достигает этого сообщения, он создает frame в стеке с вызовом функции для обработчика события click в jsfiddle.

И вот тут это становится интересным. Мы настолько привыкли считать Javascript асинхронным, что склонны упускать из виду этот крошечный факт: любой кадр должен быть выполнен полностью, прежде чем следующий кадр может быть выполнен. Нет параллелизма, люди.

Что это значит? Это означает, что всякий раз, когда функция вызывается из очереди сообщений, она блокирует очередь до тех пор, пока сгенерированный ею стек не будет очищен. Или, в более общих чертах, он блокируется, пока функция не вернется. И он блокирует все, включая операции рендеринга DOM, прокрутки и еще много чего. Если вам нужно подтверждение, просто попробуйте увеличить продолжительность длительной операции в скрипте (например, запустить внешний цикл еще 10 раз), и вы заметите, что во время его работы вы не можете прокручивать страницу. Если он работает достаточно долго, ваш браузер спросит вас, хотите ли вы убить процесс, потому что он делает страницу не отвечающей. Кадр выполняется, и цикл событий и очередь сообщений застряли до его завершения.

Так почему же этот побочный эффект текста не обновляется? Потому что в то время как вы изменили значение элемента в DOM - вы можете console.log() его значение сразу после его изменения и видеть, что он был изменен (который показывает, почему ДВК объяснение не является правильным) - браузер ждет для стека истощать (on обработчик функцию возвращать) и, таким образом, сообщение до конца, так что он может в конечном итоге получить вокруг выполнения сообщения, которое было добавлено исполняющим как реакция на нашу операцию мутации, и для того, чтобы отразить эту мутацию в пользовательском интерфейсе.

Это потому, что мы на самом деле ждем завершения кода. Мы не сказали "кто-то получит это, а затем вызовет эту функцию с результатами, спасибо, и теперь я закончил с возвращением imma, делайте что угодно сейчас", как мы обычно делаем с нашим основанным на событиях асинхронным Javascript. Мы вводим функцию-обработчик события щелчка, мы обновляем элемент DOM, вызываем другую функцию, другая функция работает долгое время и затем возвращается, затем мы обновляем тот же элемент DOM, а затем возвращаемся из исходной функции, фактически опустошая стек. И тогда браузер может перейти к следующему сообщению в очереди, которое вполне может быть сообщением, сгенерированным нами путем запуска некоторого внутреннего события типа "on-DOM-mutation".

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

Почему тогда работает setTimeout? Это происходит потому, что он эффективно удаляет вызов долго выполняющейся функции из своего собственного фрейма, планируя его последующее выполнение в контексте window, чтобы он сам мог немедленно вернуться и позволить очереди сообщений обрабатывать другие сообщения. И идея заключается в том, что сообщение "при обновлении" пользовательского интерфейса, которое было сгенерировано нами в Javascript при изменении текста в DOM, теперь опережает сообщение, помещенное в очередь для долго выполняющейся функции, поэтому обновление пользовательского интерфейса происходит до того, как мы заблокируем в течение долгого времени.

Обратите внимание, что а) долго выполняющаяся функция все еще блокирует все, когда она выполняется, и б) вы не гарантированы, что обновление пользовательского интерфейса действительно опережает его в очереди сообщений. В моем браузере Chrome в июне 2018 года значение 0 не "решает" проблему, которую демонстрирует скрипка - 10 делает. Я на самом деле немного задушен этим, потому что мне кажется логичным, что сообщение об обновлении пользовательского интерфейса должно быть поставлено в очередь перед ним, так как его триггер выполняется перед планированием долгосрочной функции, которая будет запущена "позже". Но, возможно, в движке V8 есть некоторые оптимизации, которые могут помешать, или, может быть, мне просто не хватает понимания.

Итак, в чем проблема с использованием setTimeout и какое лучшее решение для этого конкретного случая?

Прежде всего, проблема с использованием setTimeout в любом обработчике событий, подобном этому, чтобы попытаться смягчить другую проблему, склонна к путанице с другим кодом. Вот реальный пример из моей работы:

Коллега, в неправильном понимании цикла обработки событий, попытался "обработать" Javascript, используя некоторый код рендеринга шаблона, используя setTimeout 0 для его рендеринга. Он больше не хочет здесь спрашивать, но я могу предположить, что, возможно, он вставил таймеры для измерения скорости рендеринга (что было бы возвращением непосредственности функций) и обнаружил, что использование этого подхода приведет к невероятно быстрой реакции этой функции.

Первая проблема очевидна; вы не можете использовать javascript, поэтому вы ничего не выиграете, пока добавляете запутывание. Во-вторых, вы теперь эффективно отсоединили рендеринг шаблона от стека возможных прослушивателей событий, которые могут ожидать, что этот шаблон был отрендерен, хотя вполне возможно, что это не так. Реальное поведение этой функции было теперь недетерминированным, как и, по незнанию, любая функция, которая будет запускать ее или зависеть от нее. Вы можете делать обоснованные предположения, но вы не можете должным образом кодировать его поведение.

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

Но что мы можем сделать вместо этого? Что ж, как указано в упомянутой статье MDN, либо разделите работу на несколько сообщений (если можете), чтобы другие сообщения, находящиеся в очереди, могли чередоваться с вашей работой и выполнялись во время ее выполнения, либо используйте веб-работника, который может запустить в тандеме с вашей страницей и возвращать результаты, когда закончите с ее расчетами.

О, и если вы думаете: "Ну, я не могу просто добавить обратный вызов в длительную функцию, чтобы сделать ее асинхронной?", Тогда нет. Обратный вызов не делает его асинхронным, ему все равно придется выполнить долго выполняющийся код, прежде чем явно вызвать ваш обратный вызов.

Ответ 11

Другое дело - нажать вызов функции в нижней части стека, предотвращая переполнение стека, если вы рекурсивно вызываете функцию. Это приводит к циклу while, но позволяет механизму JavaScript запускать другие асинхронные таймеры.

Ответ 12

Ответы на выполнение циклов и рендеринг DOM перед выполнением некоторых других кодов верны. Нулевые вторичные тайм-ауты в JavaScript помогают сделать код псевдо-многопоточным, хотя это не так.

Я хочу добавить, что значение BEST для перекрестного браузера/кросс-платформенного нулевого времени ожидания в JavaScript на самом деле составляет около 20 миллисекунд вместо 0 (ноль), поскольку многие мобильные браузеры не могут регистрировать тайм-ауты, размер которых меньше 20 миллисекунд для ограничения часов на чипах AMD.

Кроме того, длительные процессы, которые не связаны с манипулированием DOM, теперь должны быть отправлены веб-работникам, поскольку они обеспечивают истинное многопоточное выполнение JavaScript.

Ответ 13

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

Ответ 14

Некоторые другие случаи, когда setTimeout полезен:

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

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

Ответ 15

setTimout on 0 также очень полезен в шаблоне настройки отложенного обещания, которое вы хотите сразу вернуть:

myObject.prototype.myMethodDeferred = function() {
    var deferredObject = $.Deferred();
    var that = this;  // Because setTimeout won't work right with this
    setTimeout(function() { 
        return myMethodActualWork.call(that, deferredObject);
    }, 0);
    return deferredObject.promise();
}

Ответ 16

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

  1. setTimeout() заставляет событие быть асинхронным, поэтому выполняется после всего синхронного кода, давая вашему элементу больше времени для загрузки. Асинхронные обратные вызовы, такие как обратный вызов в setTimeout(), помещаются в очередь событий и помещаются в стек циклом событий после того, как стек синхронного кода пуст.
  2. Значение 0 для ms в качестве второго аргумента в функции setTimeout() часто немного выше (4-10 мс в зависимости от браузера). Это немного большее время, необходимое для выполнения обратных вызовов setTimeout() вызвано количеством 'тиков' (когда тик толкает обратный вызов в стек, если стек пуст) цикла обработки событий. Из-за производительности и срока службы батареи количество тактов в цикле событий ограничено определенной величиной менее 1000 раз в секунду.

Ответ 18

Javascript является однопоточным приложением, поэтому не позволяет одновременно запускать функции, поэтому для достижения этого используются циклы событий. Так что именно то, что setTimeout (fn, 0) делает это, помещается в квест задачи, который выполняется, когда ваш стек вызовов пуст. Я знаю, что это объяснение довольно скучно, поэтому я рекомендую вам просмотреть это видео, оно поможет вам, как все работает в браузере. Проверьте это видео: - https://www.youtube.com/watch?time_continue=392&v=8aGhZQkoFbQ