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

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

Он говорит:

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

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

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

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

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

22 ответов


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

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

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

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

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

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

Web Inspector

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

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

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

иногда вы можете получить это, если вы случайно импортируете / вставляете один и тот же файл JS дважды, стоит проверить на вкладке ресурсов инспектора:)


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

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

вместо:

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

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


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

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

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


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

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

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


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

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

ударил Performance tab, убедится и вы получите что-то вроде этого.

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

enter image description here

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

enter image description here


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

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

enter image description here


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


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

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


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

Эл.это происходит()

on click event:

 $(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>

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


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


оба вызова идентичного кода ниже, если уменьшено на 1, работают в Chrome 32 на моем компьютере, например 17905 vs 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);

вы можете найти рекурсивную функцию в браузере crome, нажмите ctrl + shift+j, а затем вкладку source, которая дает вам поток компиляции кода, и вы можете найти с помощью точки останова в коде.


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

<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]);
          }

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

enter image description here

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


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

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

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

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

<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");
});

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

  • переместите ребенка на внешнюю сторону родителя.
  • применить это происходит функция дочернего элемента.

У меня была эта ошибка, потому что у меня было две функции JS с тем же именем


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


другой вариант, который вызывает эту ошибку Если вы конвертируете в json с сокетом.

export class User {
  constructor(socket: Socket, name: string) { 
     this.socket = socket;
     this.name   = name;
  }

   name   : string;
   socket : Socket;
}

...
let json: string = JSON.stringify(user);

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


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

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

String(socket.id);

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

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' },
    ]
  }
];