Ошибка превышения максимального размера стека вызовов


533

Я использую файл библиотеки JavaScript Direct Web Remoting (DWR) и получаю сообщение об ошибке только в Safari (для ПК и iPad)

Это говорит

Превышен максимальный размер стека вызовов.

Что именно означает эта ошибка и останавливает ли она обработку полностью?

Также любое исправление для Safariбраузера (на самом деле iPad Safari, он говорит

JS: превышено время ожидания выполнения

я предполагаю, что это та же проблема стека вызовов)


2
Я получил эту ошибку при попытке отправить переменные (не объявляя их) через dataAJAX. Исправлена ​​ошибка с объявлением переменных.
phpfresher

Ответы:


621

Это означает, что где-то в вашем коде вы вызываете функцию, которая в свою очередь вызывает другую функцию и так далее, пока не достигнете предела стека вызовов.

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

Просмотр стека

Рассмотрим этот код ...

(function a() {
    a();
})();

Вот стек после нескольких вызовов ...

Веб-инспектор

Как видите, стек вызовов увеличивается до тех пор, пока не достигнет предела: размер стека в жестком коде браузера или исчерпание памяти.

Чтобы исправить это, убедитесь, что у вашей рекурсивной функции есть базовый случай, который можно встретить ...

(function a(x) {
    // The following condition 
    // is the base case.
    if ( ! x) {
        return;
    }
    a(--x);
})(10);

6
Огромное спасибо за это ... Так есть ли способ / инструмент, которым я могу, чтобы предел стека был исчерпан ... Опять же, так как это огромный библиотечный файл, и я не создан, я не совсем понимаю, где все может пойти не так ..
testndtv

52
@ Оливер Если вам нужно запустить функцию почти 300 триллионов раз, вы, вероятно, делаете что-то не так.
ceejayoz

3
@ Оливер - вы можете захотеть взглянуть на динамическое программирование - я думаю, что у вас не так много уникальных решений, вы повторяете шаги, поэтому вам, возможно, придется запрограммировать его на полиномиальное время, а не на квадратичное. en.wikipedia.org/wiki/Dynamic_programming
newshorts

4
@Akhoy В обычной функции да, но подумайте о рекурсивной функции. Он вызывает другой (сам по себе) и оставляет адрес возврата первого в стеке (в противном случае, где бы ПК знал, куда идти?) Это, конечно, вызывает себя снова, каждый раз помещая адрес возврата в стек. Когда это исчезает из-за базового случая, который вряд ли встретится в следующем столетии, вы получите переполнение стека.
Алекс

5
Ваш код выполняет тот же неэффективный сегмент 134217728 раз. Не шутишь медленно. Вы должны использовать побитовые операторы, чтобы вы могли развернуть 9 уровней циклов в один уровень циклов.
Джек Гиффин,

82

Иногда вы можете получить это, если вы случайно импортируете / внедрите один и тот же файл JavaScript дважды, стоит проверить это на вкладке ресурсов инспектора.


9
Эта проблема обычно возникает, когда вы случайно импортируете / внедрите один и тот же файл JS дважды. Точный процесс, который вызывает рекурсивный цикл, - это не то, о чем я хотел бы спекулировать, однако я бы предположил, что это что-то вроде вождения двух одинаковых автомобилей на скорости 100 миль в час через один и тот же платный шлюз.
lucygenik

3
Я не думаю, что это так. Если встроены два одинаковых вида функций или переменных, последний переопределит предыдущие определения.
ссударака

Да ... Я действительно не знаю, почему это происходит или каков этот процесс, я бы хотел, но он бы тоже перекрыл.
lucygenik

Немного опоздал на вечеринку, но предположил бы, что когда код находится за пределами какой-либо функции, оба будут выполняться внутри двух файлов JS, и один из них, вероятно, не будет перезаписывать друг друга, поскольку они находятся вне какой-либо функции.
Киллиан Коллинз

1
@lucygenik В следующий раз, когда вы захотите быть более осторожным, одобряя изменения троллей в своих сообщениях. Этот пост почти был удален как спам (история проверок)
Жан-Франсуа Фабр

57

В моем случае я отправлял элементы ввода вместо их значений:

$.post( '',{ registerName: $('#registerName') } )

Вместо:

$.post( '',{ registerName: $('#registerName').val() } )

Это заморозило мою вкладку Chrome до такой степени, что даже не отображало диалоговое окно «Ждать / убивать», когда страница перестала отвечать на запросы ...


3
Интересно, почему не существует исключения для такой ошибки ... Спасибо!
Джурис

Большое спасибо, это спасло меня
Eme

Я собирался добавить это как возможное решение. Я только что сделал это точно LOL
Майкл Бучок

31

Где-то в вашем коде есть рекурсивный цикл (т.е. функция, которая в конечном итоге вызывает себя снова и снова, пока стек не заполнится).

Другие браузеры либо имеют большие стеки (так что вместо этого вы получаете тайм-аут), либо они по какой-то причине проглатывают ошибку (может быть, неправильно размещенный try-catch).

Используйте отладчик для проверки стека вызовов при возникновении ошибки.


Большое спасибо за ваш ответ. В IE / FF код работает нормально ... Только в настольных Safari и iPad Safari я получаю сообщение об ошибке. На самом деле файл JS не является моим собственным созданием, а представляет собой файл библиотеки (из DWR engine.js) .. directwebremoting.org Также, когда вы говорите «отладчик для проверки стека вызовов», как мне это сделать с помощью инспектора Safari?
testndtv

У меня нет опыта работы с Safari Inspector. Попробуйте открыть отладчик JavaScript и загрузить свою страницу. Отладчик должен остановиться, когда выдается неперехваченная ошибка. Если это не сработает, спросите об этом у суперпользователя или веб-мастера (см. Внизу страницы)
Аарон Дигулла

было 2 функции, названные одинаково! DOH!
jharris8567

16

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

Я нашел некоторые из новых инструментов отладки Chrome полезными для этого.

Нажмите Performance tab, убедитесь, что Javascript samplesвключены, и вы получите что-то вроде этого.

Это довольно очевидно, где переполнение здесь! Если вы нажмете на кнопку, extendObjectвы сможете увидеть точный номер строки в коде.

введите описание изображения здесь

Вы также можете увидеть время, которое может или не может быть полезным или красная сельдь.

введите описание изображения здесь


Еще один полезный трюк, если вы не можете найти проблему, - это поместить множество console.logутверждений там, где вы думаете, что проблема. Предыдущий шаг выше может помочь вам в этом.

В Chrome, если вы неоднократно выводите одинаковые данные, он будет отображаться так, как показано, где проблема более ясна. В этом случае стек достиг 7152 фреймов, прежде чем окончательно рухнул:

введите описание изображения здесь


13

В моем случае я преобразовывал большой байтовый массив в строку, используя следующее:

String.fromCharCode.apply(null, new Uint16Array(bytes))

bytes содержал несколько миллионов записей, что слишком велико для размещения в стеке.


У меня тоже была такая же проблема с той же строкой !! Thanx
КШ

так что является лучшим решением для преобразования большого байтового массива ??
КШ

6
@KarthikHande Вы должны сделать это в цикле for, а не в одном вызове. var uintArray = new Uint16Array(bytes); var converted = []; uintArray.forEach(function(byte) {converted.push(String.fromCharCode(byte))});
Нейт Гленн

Как мне преобразовать это в JSON? Я пробовал JSON.parse, но он не работал ... Я использую Uintl8Array
ksh

@KarthikHande Я не могу сказать, не видя всей проблемы. Пожалуйста, откройте еще один вопрос со всеми деталями.
Нейт Гленн

6

В моем случае событие click распространялось на дочерний элемент. Итак, мне пришлось поставить следующее:

e.stopPropagation ()

по событию клика:

 $(document).on("click", ".remove-discount-button", function (e) {
           e.stopPropagation();
           //some code
        });
 $(document).on("click", ".current-code", function () {
     $('.remove-discount-button').trigger("click");
 });

Вот HTML-код:

 <div class="current-code">                                      
      <input type="submit" name="removediscountcouponcode" value="
title="Remove" class="remove-discount-button">
   </div>

5

Проверьте подробности ошибки в консоли панели инструментов Chrome dev, это даст вам функции в стеке вызовов и проведет вас к рекурсии, которая вызывает ошибку.


5

Если по какой-то причине вам нужен бесконечный процесс / рекурсия, вы можете использовать веб-работника в отдельном потоке. http://www.html5rocks.com/en/tutorials/workers/basics/

если вы хотите манипулировать элементами dom и перерисовывать, используйте анимацию http://creativejs.com/resources/requestanimationframe/


3

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

node --stack-size=2000

Смотрите также: Как я могу увеличить максимальный размер стека вызовов в Node.js


2

В моем случае два мода jQuery показывали сложенными друг на друга. Предотвращение, которое решило мою проблему.


2

Недавно мы добавили поле на сайт администратора, над которым мы работаем - contact_type ... easy right? Что ж, если вы вызываете select "type" и пытаетесь отправить его через вызов jquery ajax, это завершится неудачно с этой ошибкой, скрытой глубоко в jquery.js. Не делайте этого:

$.ajax({
    dataType: "json",
    type: "POST",
    url: "/some_function.php",
    data: { contact_uid:contact_uid, type:type }
});

Проблема в том, что type: type - я считаю, что мы называем аргумент «type» - наличие значения переменной с именем type не является проблемой. Мы изменили это на:

$.ajax({
    dataType: "json",
    type: "POST",
    url: "/some_function.php",
    data: { contact_uid:contact_uid, contact_type:type }
});

И переписал some_function.php соответственно - проблема решена.


1

Проверьте, есть ли у вас функция, которая вызывает сама себя. Например

export default class DateUtils {
  static now = (): Date => {
    return DateUtils.now()
  }
}

1

Это также может вызвать Maximum call stack size exceededошибку:

var items = [];
[].push.apply(items, new Array(1000000)); //Bad

Тоже самое:

items.push(...new Array(1000000)); //Bad

Из документов Mozilla :

Но будьте осторожны: при использовании применять этот способ, вы рискуете превысить ограничение длины аргумента движка JavaScript. Последствия применения функции со слишком большим количеством аргументов (например, более десятков тысяч аргументов) варьируются в зависимости от движков (JavaScriptCore имеет жестко запрограммированный предел аргументов 65536), потому что это предел (в действительности, даже характер любого чрезмерно большого стека) поведение) не уточняется. Некоторые двигатели будут выбрасывать исключения. Более пагубно, другие будут произвольно ограничивать количество аргументов, фактически передаваемых прикладной функции. Чтобы проиллюстрировать этот последний случай: если бы такой механизм имел ограничение в четыре аргумента (фактические пределы, конечно, значительно выше), это было бы так, как если бы аргументы 5, 6, 2, 3 были переданы для применения в приведенных выше примерах, а не полный массив.

Поэтому постарайтесь:

var items = [];
var newItems = new Array(1000000);
for(var i = 0; i < newItems.length; i++){
  items.push(newItems[i]);
}

0

Оба вызова идентичного кода ниже, если его уменьшить на 1, работают в Chrome 32 на моем компьютере, например, 17905 против 17904. При запуске как есть они выдают ошибку «RangeError: Превышен максимальный размер стека вызовов». Похоже, что этот предел не является жестко заданным, но зависит от аппаратного обеспечения вашей машины. Похоже, что если он вызывается как функция, то этот добровольный предел выше, чем если бы он вызывался как метод, т.е. этот конкретный код использует меньше памяти при вызове как функция.

Вызывается как метод:

var ninja = {
    chirp: function(n) {
        return n > 1 ? ninja.chirp(n-1) + "-chirp" : "chirp";
    }
};

ninja.chirp(17905);

Вызывается как функция:

function chirp(n) {
    return n > 1 ? chirp( n - 1 ) + "-chirp" : "chirp";
}

chirp(20889);

0

Вы можете найти свою рекурсивную функцию в браузере crome, нажмите Ctrl + Shift + J, а затем вкладку исходного кода, которая дает вам процесс компиляции кода, и вы можете найти, используя точку останова в коде.


Однако иногда бывает сложно понять, откуда возникла ошибка. У нас много JavaScript на нашем сайте.
Матиас Ликкегор Лоренцен,

0

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

<div>
      <div class="uploader greyLogoBox" id="uploader" flex="64" onclick="$('#filePhoto').click()">
        <img id="imageBox" src="{{ $ctrl.companyLogoUrl }}" alt=""/>
        <input type="file" name="userprofile_picture"  id="filePhoto" ngf-select="$ctrl.createUploadLogoRequest()"/>
        <md-icon ng-if="!$ctrl.isLogoPresent" class="upload-icon" md-font-set="material-icons">cloud_upload</md-icon>
        <div ng-if="!$ctrl.isLogoPresent" class="text">Drag and drop a file here, or click to upload</div>
      </div>
      <script type="text/javascript">
          var imageLoader = document.getElementById('filePhoto');
          imageLoader.addEventListener('change', handleImage, false);

          function handleImage(e) {
              var reader = new FileReader();
              reader.onload = function (event) {

                  $('.uploader img').attr('src',event.target.result);
              }
              reader.readAsDataURL(e.target.files[0]);
          }
      </script>
      </div>

CSS.css

.uploader {
  position:relative;
  overflow:hidden;
  height:100px;
  max-width: 75%;
  margin: auto;
  text-align: center;

  img{
    max-width: 464px;
    max-height: 100px;
    z-index:1;
    border:none;
  }

  .drag-drop-zone {
    background: rgba(0, 0, 0, 0.04);
    border: 1px solid rgba(0, 0, 0, 0.12);
    padding: 32px;
  }
}

.uploader img{
  max-width: 464px;
  max-height: 100px;
  z-index:1;
  border:none;
}



.greyLogoBox {
  width: 100%;
  background: #EBEBEB;
  border: 1px solid #D7D7D7;
  text-align: center;
  height: 100px;
  padding-top: 22px;
  box-sizing: border-box;
}


#filePhoto{
  position:absolute;
  width:464px;
  height:100px;
  left:0;
  top:0;
  z-index:2;
  opacity:0;
  cursor:pointer;
}

до исправления мой код был:

function handleImage(e) {
              var reader = new FileReader();
              reader.onload = function (event) {
                  onclick="$('#filePhoto').click()"
                  $('.uploader img').attr('src',event.target.result);
              }
              reader.readAsDataURL(e.target.files[0]);
          }

Ошибка в консоли:

введите описание изображения здесь

Я решил это, удалив onclick="$('#filePhoto').click()"из тега div.


Где вы добавите щелчок ()
TSEA

0

Я столкнулся с той же проблемой, я решил ее, удалив имя поля, которое использовалось дважды на AJAX, например

    jQuery.ajax({
    url : '/search-result',
    data : {
      searchField : searchField,
      searchFieldValue : searchField,
      nid    :  nid,
      indexName : indexName,
      indexType : indexType
    },
.....

0

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

Предположим, у вас есть такие вложенные элементы:

<a href="#" id="profile-avatar-picker">
    <span class="fa fa-camera fa-2x"></span>
    <input id="avatar-file" name="avatar-file" type="file" style="display: none;" />
</a>

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

Так что этот код потерпит неудачу:

$('#profile-avatar-picker').on('click', (e) => {
    e.preventDefault();

    $('#profilePictureFile').trigger("click");
});

У вас есть два варианта, чтобы избежать этого:

  • Переместите ребенка наружу от родителя.
  • Примените функцию stopPropagation к дочернему элементу.


0

Если вы работаете с картами Google, то проверьте, правильно ли передаются new google.maps.LatLngданные о широте. В моем случае они передавались как неопределенные.


0

Проблема в моем случае заключается в том, что у меня есть дочерний маршрут с тем же путем, что и родительский:

const routes: Routes = [
  {
    path: '',
    component: HomeComponent,
    children: [
      { path: '', redirectTo: 'home', pathMatch: 'prefix' },
      { path: 'home', loadChildren: './home.module#HomeModule' },
    ]
  }
];

Поэтому мне пришлось убрать линию детского маршрута

const routes: Routes = [
  {
    path: '',
    component: HomeComponent,
    children: [
      { path: 'home', loadChildren: './home.module#HomeModule' },
    ]
  }
];

0

в моем случае я получаю эту ошибку при вызове ajax, и данные, которые я пытался передать этой переменной, не определены, то есть показывает эту ошибку, но не описывает эту переменную, не определенную. Я добавил определил, что переменная n получила значение.


Я был сожжен таким же образом, в моем случае это потому, что я набрал имя переменной ... фактически то же самое.
user2366842

0

Столкнулся с той же проблемой, не понимая, что не так, начал обвинять Бабеля;)

Наличие кода, не возвращающего никаких исключений в браузерах:

if (typeof document.body.onpointerdown !== ('undefined' || null)) {

проблема была плохо создана || (или) part, поскольку babel создает собственную проверку типа:

function _typeof(obj){if(typeof Symbol==="function"&&_typeof(Symbol.iterator)==="symbol")

так что удаление

|| null

сделал вавилонскую транспиляцию.


0

В моем случае я по ошибке присвоил одно и то же имя переменной и функции val "class_routine_id"

var class_routine_id = $("#class_routine_id").val(class_routine_id);

должно быть вроде:

 var class_routine_id = $("#class_routine_id").val(); 


0

Я использую React-Native 0.61.5 вместе с ( npm 6.9.0 & node 10.16.1 )

Пока я устанавливаю любые новые библиотеки в Project, я получил

(например, npm install @ реагировать-навигация / родной --save)

Ошибка превышения максимального размера стека вызовов

для этого я стараюсь

sudo npm cache очистить --force

(Примечание: - Команда ниже, как правило, занимает от 1 до 2 минут в зависимости от размера кэша npm )


-1

Иногда это происходит из-за типа данных для преобразования, например, у вас есть объект, который вы рассматриваете как строку.

socket.id в nodejs либо в клиенте js, например, не является строкой. чтобы использовать его как строку, вы должны добавить слово String перед:

String(socket.id);

-1

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

Объявление переменной исправило мою ошибку.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.