Pull to refresh

Comments 32

Упомянул в конце поста о такой возможности.
вместо new Date().getTime() можно написать просто Date.now()
UFO just landed and posted this here
Не работает, кстати, в <=IE8
Можно обойтись и без него.
>elapsed = Math.floor(time / 100) / 10;
>if(Math.round(elapsed) == elapsed) { elapsed += '.0'; }
Заменяется на elapsed = (time / 1000).toFixed(1);
В случае когда интервалы должны быть строго (ну или почти строго) равными, при этом с переменной загрузкой внутри, нет, не дает. Безбожно скачет.
Да, все верно. Только точность от этого нисколько не выше и не ниже, это просто механизм реализации. А в посте мы гонимся именно за коррекцией таймера, для повышения стабильности периодичности срабатывания.
printercu, кратко — нет. setTimeout более гибкий. Преположим Вы хотите делать polling на свой сервер каждые 500 миллисекунд, при это гарантируя, что два запроса на сервер не поступит одновременно.
Выполнить это с setInterval невозможно, т.к. если вы не получили ответ через 500 миллисекунд, setInterval пошлет еще раз запрос.
Однако вместо setInterval использовать setTimеout в success callback каждого запроса за вычетом времени на запрос, можно гарантировать, что в среднем запросы будут уходить каждый 500 миллисекунд и исключительно один за одним.
Вопрос был не в разнице. Я просто предположил, что с названием «setInterval» этот таймер должен соблюдать интервалы.
Небольшой пример того, что все можно и с setInterval.
working = false
setInterval ->
  return if working
  working = true
  $.get '/smth', -> working = false
, 200
Вы не правы, setInterval не гарантирует что код выполнится через 200 мс, скорее через 199+.
[offtop]
Какой приятный фон на первой картинке. Думаю именно таким должен быть фон в современном UI. А чистые цвета — это такое-же недоразумение как и светлый цвет фона для текста (пример тому Adobe Lightroom, Microsoft Visual Studio, IntelliJ Idea и др.)
[/offtop]
На вкус и цвет…
Оф-топ: Однако, частично согласен с вашей мыслью — неоднородность фона создаёт некий комфорт, в отличие от «плиточных» дизайнов.
[minute of a courtesy]
Этим комментарием я ставлю вам плюсик, ибо абсолютно с тем же мнением :)
[/minute of a courtesy]
А где защита от скрытых замыканий?

Запомните на всю жизнь эту конструкцию:
var timeout = null;

timeout = setTimeout([function], [time]);

function [function]() {
if (timeout) clearTimeout(timeout);
}
//предполагается, что это будет находится в теле функции
//или методе, в которой/ом, будет устанавливатся таймер.

//очищаем объект в котором лежал счетчик, если он уже существовал до этого.
var timeout = null;

//инициализируем новый счетчик
timeout = setTimeout([function], [time]);

//как только интервал закончился, останавливаем счетчик
function [function]() {
if (timeout) clearTimeout(timeout);
}

Я еще не слишком полно знаком с тем, как кушает память setTimeout(), но этот код указывает видимо на то, что все таймеры из моих примеров оставались в памяти, хоть ссылка на них уже и не была доступна. А этот метод позволяет однозначно установить соответствие одного таймера одной итерации, без накопления лишних и без возможности «временного перекоса», когда предыдущий вызов еще не закончен, а уже наступил следующий.
Суть в том, что если при выполнении функции вызванной по setTimeout, что-то пойдёт не так (ошибка, зависание и пр.), то работа setTimeout после истечения времени будет продолжаться. setTimeout — это фактически замыкание, которое при неправильной работе вложенной функции превращается в скрытое замыкание. Браузер будет продолжать выделять память под «зависший» setTimeout — возникает утечка памяти. Замечу, что привести к такой ситуации могут не только ошибки в коде, но и внешние факторы (например, зависание браузера при обработке большой страницы в соседней вкладке).

Приведённая мной конструкция данную ситуацию исправляет привязкой setTimeout к конкретной переменной и потом удаление setTimeout посредством clearTimeout и сохранённого значения переменной.

На практике абсолютное большинство JS-программистов не знает об этой особенности setTimeout (так же как и о замыканиях).

И похоже это абсолютное большинство в данный момент сливает мне карму. Хабр уже не тот…
Вы имеете ввиду что если callback-функция таймаута свалится с исключением, то GC не сможет собрать ее?
Я просто не понимаю, что может «пойти не так» внутри callback-функции?
Не обязательно с исключением. Достаточно некой «задержки» выполнения кода внутри её. Такая ситуация возникает, если в момент удаления замыкания setTimeout (которое происходит согласно внутренним механизмам браузера), callback-функция ещё выполняется.

Как я написал выше, данная «задержка» может возникнуть из-за внешних факторов. То есть даже, если код и входные данные абсолютно правильные, setTimeout может всё равно «зависнуть». Такова природа замыканий в JS
Что вы подразумеваете под «замыканиями»? Мне всегда казалось что «замыкание» — это когда функция внутри себя имеет ссылку на переменную, которая не определена внутри нее.

И что значит «задержка» выполнения кода?
Чтобы не было путаницы, поясню — то, что я называл setTimeout замыканием, не совсем корректно. Замыканием в JS принято называть именно пользовательские функции. Но setTimeout ведёт себя аналогично замыканиям, поэтому я так оговаривался.

Теперь посмотрите на своё определение «замыкание — это когда функция внутри себя имеет ссылку на переменную, которая не определена внутри нее» и на работу setTimeout. setTimeout — это системная функция, которая внутри себя имеет ссылку на функцию, которая не определена внутри неё.

По поводу «задержки» выполнения кода:
Скрытое замыкание возникает, когда функцию-замыкание каким-либо образом уничтожают, но код внутри нее ещё выполняется. В результате функция как бы «исчезает» (не видна в никакой области видимости и к ней невозможно обратиться), но продолжает работу.
Если подумать логично, то при уничтожении функции либо должно прерваться выполнение этого кода, либо «исчезать» после выполнения кода до конца. Но в JS всё организовано нелогично — это баг/фитча.
О setTimeout говорить сложно так, как сама эта функция уничтожается внутренним механизмом браузера, который не контролируется программистом.
Вы не до конца понимаете «однопоточную» природу работы интерпретатора JS. Функции выполняются интерпретатором по-очереди, каждая — от начала и до конца. Если функция УЖЕ выполняется интерпретатором, ничто прервать ее не может. На этом основана одна из наиболее сильных сторон JS — асинхронность выполнения запросов полностью без всяких паттернов синхронизации потоков, присущих «большим» языкам (мьютексы, семафоры и т.д.).

setTimeout принимает два параметра — callback и timeout. И ставит на выполнение в основной интерпретатор функцию callback, которая должна отработать когда интерпретатор освободится, но не меньше чем через время timeout. Если к этому времени вы вызвали clearTimeout соответствующему таймауту, то выполнения функции не будет. При этом clearTimeout вы можете вызвать только в одной из функций, чье выполнение по очереди было раньше, чем выполнение callback-функции таймаута.

При чем тут замыкания, я не совсем понимаю. Функция в JS либо выполняется до конца, либо падает с исключением. Или вы можете влепить что-то вроде while(1) {} в коде callback-функции но в этом случае вы тупо повесите интерпретатор. Совсем. И до конца. В каком месте тут может происходить утечка памяти, я тоже не понимаю.
Все еще тот, пост рассчитан на новичков. Просто здесь нужно давать немного более развернутые пояснения. Извините за карму :(
работа setTimeout после истечения времени будет продолжаться

Почему? В моем комментарии выше есть ссылка на спецификацию. Даже setInterval(function(){throw 'asd'}, 500) сработает только 1 раз.
Проверил на ноде:
> u = []
> (function(){ var i, a = function(){ var t = setTimeout(function(){ clearTimeout(t); throw 'err' }, 100) }; for (i = 0; i <= 1000; i++) a() })()
// тут err много раз
> u.push(process.memoryUsage())

> u
[ { rss: 22462464, heapTotal: 16702208, heapUsed: 6784336 },
  { rss: 27422720, heapTotal: 17734144, heapUsed: 7669848 },
  { rss: 27914240, heapTotal: 17734144, heapUsed: 8310264 },
  { rss: 28450816, heapTotal: 26122752, heapUsed: 8936288 },
  { rss: 32952320, heapTotal: 26122752, heapUsed: 9413880 } ]

// то же для 
(function(){ var i, a = function(){ var t = setTimeout(function(){ throw 'err' }, 100) }; for (i = 0; i <= 1000; i++) a() })()

> u
[ { rss: 23154688, heapTotal: 16702208, heapUsed: 6874448 },
  { rss: 28086272, heapTotal: 16702208, heapUsed: 7206640 },
  { rss: 28798976, heapTotal: 17734144, heapUsed: 7408280 },
  { rss: 29429760, heapTotal: 26122752, heapUsed: 7655528 },
  { rss: 33726464, heapTotal: 26122752, heapUsed: 7461968 } ]

Я не так что-то делаю?
А метод нарочно назван preciousTimer или имелось в виду preciseTimer?
Да, точно. Во-первых это самая важная часть в коде, во вторых мне пришлось сравнительно много понять, прежде чем прийти к этому. А во-вторых, на деле он не такой уж и точный в этом контексте, как ни печально.
Sign up to leave a comment.

Articles