Найти тему
Журнал «Код»

Что означает ошибка Uncaught RangeError: Maximum call stack size exceeded

Оглавление

Это когда вызывается слишком много вложенных функций

Ситуация: заказчик попросил разместить на странице кликабельную картинку, а чтобы на неё обратило внимание больше посетителей, попросил сделать вокруг неё моргающую рамку. Логика моргания в скрипте очень простая:

  1. В первой функции находим на странице нужный элемент.
  2. Добавляем рамку с какой-то задержкой (чтобы она какое-то время была на экране).
  3. Вызываем функцию убирания рамки.
  4. Внутри второй функции находим тот же элемент на странице.
  5. Убираем рамку с задержкой.
  6. Вызываем первую функцию добавления рамки.

Код простой, поэтому делаем всё в одном файле:

-2

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

Uncaught RangeError: Maximum call stack size exceeded

Что это значит: в браузере произошло переполнение стека вызовов и из-за этого он не может больше выполнять этот скрипт.

Переполнения стека простыми словами означает вот что:

Когда компьютер что-то делает, он это делает последовательно — 1, 2, 3, 4.

  1. Иногда ему нужно отвлечься от одного и сходить сделать что-то другое — а, б, в, г, д. Получается что-то вроде 1, 2, 3 → а, б, в, г, д → 4.
  2. Вот эти переходы 3 → а и д → 4 — это компьютеру нужно запомнить, что он выполнял пункт 3, и потом к нему вернуться.
  3. Каждое запоминание, что компьютер бросил и куда ему нужно вернуться, — это называется «вызов».
  4. Вызовы хранятся в стеке вызовов. Это стопка таких ссылок типа «когда закончишь вот это, вернись туда».
  5. Стек не резиновый и может переполняться.

Что делать с ошибкой Uncaught RangeError: Maximum call stack size exceeded

Эта ошибка — классическая ошибка переполнения стека во время выполнения рекурсивных функций.

Рекурсия — это когда мы вызываем функцию внутри самой себя, но чуть с другими параметрами. Когда параметр дойдёт до конечного значения, цепочка разматывается обратно и функция собирает вместе все значения. Это удобно, когда у нас есть чёткий алгоритм подсчёта с понятными правилами вычислений.

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

  1. Функции начинают бесконтрольно вызывать себя бесконечное число раз.
  2. Стек вызовов начинает запоминать вызов каждой функции, чтобы, когда она закончится, вернуться к тому, что было раньше.
  3. Стек — это определённая область памяти, у которой есть свой объём.
  4. Вызовы не заканчиваются, и стек переполняется — в него больше нельзя записать вызов новой функции, чтобы потом вернуться обратно.
  5. Браузер видит всё это безобразие и останавливает скрипт.

То же самое будет, если мы попробуем запустить простую рекурсию слишком много раз:

-3

Как исправить ошибку Uncaught RangeError: Maximum call stack size exceeded

Самый простой способ исправить эту ошибку — контролировать количество рекурсивных вызовов, например проверять это значение на входе. Если это невозможно, то стоит подумать, как можно переделать алгоритм, чтобы обойтись без рекурсии.

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

-4
-5