TL; DR;
Зависит от движка браузера.
В Blink и Webkit:
- Максимальное количество одновременных таймеров составляет 2 31 -1.
- Если вы попытаетесь использовать больше, ваш браузер, вероятно, зависнет из-за бесконечного цикла.
Официальная спецификация
Из документов W3C :
Метод setTimeout()
должен выполнять следующие шаги:
Пусть handle будет целым числом, определенным пользовательским агентом, которое больше нуля и будет определять время ожидания, которое будет установлено этим вызовом.
Добавить запись в список активных тайм-аутов для handle .
[...]
Также:
Каждый объект, который реализует интерфейс WindowTimers
, имеет список активных тайм-аутов и список активных интервалов . Каждая запись в этих списках идентифицируется номером, который должен быть уникальным в своем списке в течение времени жизни объекта, реализующего интерфейс WindowTimers
.
Примечание : хотя W3C упоминает два списка, WHATWG spec устанавливает, что setTimeout
и setInterval
совместно используют общие список активных таймеров . Это означает, что вы можете использовать clearInterval()
для удаления таймера, созданного с помощью setTimeout()
, и наоборот.
По сути, каждый пользовательский агент может свободно реализовывать идентификатор дескриптора по своему усмотрению, с единственным требованием, чтобы целое число было уникальным для каждого объекта; вы можете получить столько же ответов, сколько и браузерные реализации.
Давайте посмотрим, например, что делает Blink.
Blink реализация
Предыдущая заметка : не такая простая задача , чтобы найти реальный исходный код Blink. Он принадлежит кодовой базе Chromium , которая отражается в GitHub . Я буду ссылаться на GitHub (его текущий последний тег: 72.0.3598.1
), потому что это лучшие инструменты для навигации по коду. Три года назад они давили коммиты на хром / моргание / . В настоящее время активная разработка ведется на chromium / third_party / WebKit , но обсуждается о новой миграции.
В Blink (и в WebKit, который, очевидно, имеет очень похожую кодовую базу), ответственным за поддержание вышеупомянутого списка активных таймеров является DOMTimerCoordinator
, принадлежащий каждому ExecutionContext
.
// Maintains a set of DOMTimers for a given page or
// worker. DOMTimerCoordinator assigns IDs to timers; these IDs are
// the ones returned to web authors from setTimeout or setInterval. It
// also tracks recursive creation or iterative scheduling of timers,
// which is used as a signal for throttling repetitive timers.
class DOMTimerCoordinator {
DOMTimerCoordinator
сохраняет таймеры в коллекции blink::HeapHashMap
(псевдоним TimeoutMap
) timers_
, который ключ (соответствует спецификации) int
типа:
using TimeoutMap = HeapHashMap<int, Member<DOMTimer>>;
TimeoutMap timers_;
Это ответ на ваш первый вопрос (в контексте Blink): максимальное количество активных таймеров для каждого контекста составляет 2 31 -1 ; намного ниже, чем JavaScript MAX_SAFE_INTEGER
(2 53 -1), который вы упомянули, но все же более чем достаточно для обычных случаев использования.
На ваш второй вопрос: « Что происходит, вы больше не можете использовать таймауты? », у меня пока только частичный ответ.
Новые таймеры создаются DOMTimerCoordinator::InstallNewTimeout()
. Он вызывает закрытую функцию-член NextID()
для получения доступного целочисленного ключа и DOMTimer::Create
для фактического создания объекта таймера. Затем он вставляет новый таймер и соответствующий ключ в timers_
.
int timeout_id = NextID();
timers_.insert(timeout_id, DOMTimer::Create(context, action, timeout,
single_shot, timeout_id));
NextID()
получает следующий идентификатор в круговой последовательности от 1 до 2 31 -1:
int DOMTimerCoordinator::NextID() {
while (true) {
++circular_sequential_id_;
if (circular_sequential_id_ <= 0)
circular_sequential_id_ = 1;
if (!timers_.Contains(circular_sequential_id_))
return circular_sequential_id_;
}
}
Увеличивает в 1 значение circular_sequential_id_
или устанавливает его в 1, если оно выходит за пределы верхнего предела (хотя INT_MAX
+ 1 вызывает UB, большинство реализаций C возвращают INT_MIN
).
Итак, когда у DOMTimerCoordinator
заканчиваются идентификаторы, повторяется попытка с 1 до тех пор, пока не будет найден один свободный.
Но что произойдет, если они все используются? Что мешает NextID()
войти в бесконечный цикл? Это кажется, что ничего . Вероятно, разработчики Blink закодировали NextID()
в предположении, что никогда не будет 2 31 -1 таймеров одновременно. Это имеет смысл; для каждого байта, возвращаемого DOMTimer::Create()
, вам потребуется ГБ ОЗУ для хранения timers_
, если он заполнен. Это может добавить к ТБ, если вы храните длинные обратные вызовы. Не говоря уже о времени, необходимом для их создания.
В любом случае, выглядит удивительно, что не реализована защита от бесконечного цикла, поэтому я связался с разработчиками Blink , но пока у меня нет ответа. Я обновлю свой ответ, если они ответят.