Firefox и setTimeout
При запуске функции в Firefox через setTimeout ей передается дополнительный параметр — время, на которое было просрочено выполнение кода.
Иногда это может привести к неожиданным последствиям, будьте осторожны, граждане!
var testFunction = function(p){
…
setTimeout(arguments.callee, n);
…
}
Через n миллисекунд будет вызвано в Firefox:
testFunction(p)
, в остальных браузерах:
testFunction().
Такое нелогичное отличие достаточно неприятно, и у меня лично привело к выпадению из функции по return в совершенно неожиданном месте. Хотя, профессионалы, наверное, объяснят мне, в чем я неправ.
А вот и полный пример:
var testFunction = function(p){
// свойство функции: сколько раз уже была вызвана
if (!arguments.callee.runTimes) arguments.callee.runTimes = 0;
arguments.callee.runTimes++;
// алерт с отладочной информацией
alert("Function called " + arguments.callee.runTimes + " time.\r\nParameters are " + ((typeof (p) == "undefined")?"not ":"") + "set.");
// если запущено менее двух раз (нам же не нужно зацикливание?)
if (arguments.callee.runTimes < 2){
// запускаем себя же через какое-то время
setTimeout(arguments.callee, 100);
}
};
// вызов функции напрямую
testFunction("some string parameter");
Спасибо Dmitry A. Soshnikov за очень дельный совет.
|
В Firefox, в setTimeout первым аргументом передаётся "просроченное время", "задержка" (если таковые возникнут) в ходе интерпретации кода. Т.е., если setTimeout не успевает запуститься после назначенного времени (например, через 100мс), эта "просроченная задержка" фиксируется первым параметром.
Далее, когда поток освободился и готов принять (вклинить) код setTimeout-a, проверяется, есть ли "просроченное время", и если есть, то код может выполнится моментально (проверить можно, если поставить alert сразу после вызова setTimeout-a и подождать некоторое время, превышающее интервал таймаута).
Простой пример:
setTimeout(function () { alert(arguments[0]); // ah? ;) }, 0);Или даже так (чтобы приблизать к Вашему случаю):
setTimeout(function (myParam) { alert(myParam); // wth? ;) }, 0);Почитать можно в багтреккере:
https://bugzilla.mozilla.org/show_bug.cgi?id=10637
https://bugzilla.mozilla.org/show_bug.cgi?id=263945
Спасибо, значение параметра посмотреть я как-то не догадался, ограничился
. Пожалуй, нужно переписать половину поста.
typeofНо, в самом деле, действительно ли нужен этот параметр?
Не думаю. В первой ссылке там в багтреккере тоже разборки, надо ли это фиксить (ещё 10 лет назад
)? Мотивировали обратной совместимостью с Netscape 4.X, но это бред какой-то (на сегодняшний день имеется в виду, поскольку этот скрытый параметр задержки остался до сих пор).
С другой стороны, как должен себя вести следующий код, в случае, если оставить alert(1) на 3-4 секунды:
setTimeout(function (param) { alert(param); }, 3000); alert(1);По идее, запрашивалось отложенное выполнение через 3 секунды (3000 миллисекунд). Второй alert отнял это время, блокируя поток. После того, как мы уничтожим alert(1), начинает работать функция setTimeout-a.
И на сегодня, во всех браузерах, кроме FF и Opera, начинается отсчёт 3000 миллисекунд (потестируйте, например, в IE или Chrome, Safari) после alert(1) (вероятно, мотивируя тем, что alert, уж точно, полностью блокирует поток и ничего выполняться не может). FF и Opera, в свою очередь, определяют это просроченное время и сокращают таймаут (в случае, если таймаут просрочен полностью, функция выполняется сразу). Поведение последних кажется более правильным (даже несмотря на полную блокировку alert-ом).
Только в Opera, в отличие от FF, не передаётся никакой параметр, портя пользовательскую функцию.
Моё мнение, зря они оставили этот параметр (явно, грязный хак просто был, и лень было фиксить потом), надо было его убрать ещё тогда, 10 лет назад.
просроченное время передается не первым, а последним аргументом
А проверить?
Мне кажется чувак не понял фразу
>В Firefox, в setTimeout первым аргументом передаётся "просроченное время"
Он имел ввиду последним аргументом в
setTimeout(function, delay), а ты же говорил про то, что это самsetTimeoutпотом вызывает функцию, передавая в неё первым аргументом это самое просроченное времяfunction f (x, y) { alert (x+":"+y); } setTimeout (f, 0, 20); alert (0);алерт покажет что-нибудь вроде 20:xxxx
так что таки последним
А дополнительные параметры в setTimeout (которые не везде и работают) не рассматривались. Впрочем, и там - не последним (но, и не всегда первым):
function f(x, y, z) { alert(x + ":" + y); } setTimeout(f, 0, 20); alert(0);function f(x, y, z) { alert(x + ":" + y); } setTimeout(f, 0); alert(0);А дополнительные параметры в setTimeout (которые не везде и работают) не рассматривались. Впрочем, и там - не последним (но, и не всегда первым):
setTimeout(f, 0, 20); передаст первым аргументом 20, вторым и последним - задержку.
setTimeout(f, 0); передаст первым и последним аргументом задержку.
А вот в функции f задержка может и не оказаться на месте последнего аргумента.
Наибольший интерес как раз и представляет функция f и положение параметра задержки в числе её параметров.
Отправить комментарий
Приветствуются комментарии:- Полезные.
- Дополняющие прочитанное.
- Вопросы по прочитанному. Именно по прочитанному, чтобы ответ на него помог другим разобраться в предмете статьи. Другие вопросы могут быть удалены.
P.S. Лучшее "спасибо" - не комментарий, как все здорово, а рекомендация или ссылка на статью.Для остальных вопросов и обсуждений есть форум.