Максимальний розмір стека викликів перевищив помилку


533

Я використовую файл бібліотеки JavaScript з прямим веб-видаленням (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
@Oliver Якщо вам доведеться запускати функцію майже 300 трильйонів разів, ви, ймовірно, робите щось не так.
ceejayoz

3
@Oliver - можливо, ви захочете вивчити динамічне програмування - я думаю, у вас немає такої кількості унікальних рішень, ви повторюєте кроки, тому, можливо, вам потрібно буде запрограмувати це на полінуміальний час, а не на квадратичний. en.wikipedia.org/wiki/Dynamic_programming
новинники

4
@Akhoy У звичайній функції так, але подумайте про рекурсивну функцію. Він викликає інше (себе) і залишає зворотну адресу колишнього на стеці (інакше куди ПК знатиме, куди йти?) Це, звичайно, викликає себе знову, кожного разу натискаючи зворотну адресу на стек. Коли це пробігає через базовий випадок, який, швидше за все, не буде досягнуто в наступному столітті, ви отримуєте переповнення стека.
alex

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

82

Іноді це можна отримати, якщо випадково імпортувати / вставляти один і той же файл JavaScript двічі, що варто перевірити на вкладці ресурсів інспектора.


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

3
Я не думаю, що це так. Якщо вбудовано дві однакові функції або змінні, остання буде перекривати попередні визначення.
ssudaraka

Так ... Я не знаю, чому це відбувається, або який процес, я б хотів, що це теж може перекрити.
lucygenik

Трохи запізнюємось на вечірку, але уявимо, що коли код знаходиться поза будь-якою функцією, обидва будуть виконуватися в межах двох файлів JS, і один, мабуть, не замінить будь-якого іншого, оскільки він знаходиться поза будь-якою функцією.
Кілліан Коллінз

1
@lucygenik наступного разу, коли ви хочете бути більш обережними при затвердженні редагувань тролів у своїх публікаціях. Цю публікацію майже видалили як спам (перевірити історію)
Жан-Франсуа Фабре

56

У моєму випадку я надсилав вхідні елементи замість їх значень:

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

Замість:

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

Це заморозило мою вкладку Chrome до такої точки, що навіть не показало мені діалогового вікна "Чекати / вбити", коли сторінка перестала відповідати ...


3
Цікаво, чому для такої помилки не є винятком ... Дякую!
Джуріс

Велике спасибі, це врятувало мене
Еме Хадо

Я збирався додати це як можливе рішення. Я щойно зробив цю точну річ, хай
Майкл Бучок

30

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

Інші веб-переглядачі або мають більший стек (тому замість цього ви отримуєте тайм-аут), або вони чомусь проковтують помилку (можливо, погано розміщена спроба лову).

Використовуйте налагоджувач, щоб перевірити стек викликів, коли сталася помилка.


Дуже багато для вашої відповіді. У IE / FF код, здається, працює нормально. Лише в настільних Safari та iPad Safari я отримую помилку. Фактично файл JS - це не моє власне створення, але це бібліотечний файл (від DWR engine.js) .. directwebremoting.org Також, коли ви говорите "налагоджувач для перевірки стека викликів", як це зробити за допомогою інспектора Safari?
testndtv

Я не маю досвіду роботи з інспектором Safari. Спробуйте відкрити налагоджувач JavaScript і завантажити свою сторінку. Відладчик повинен зупинитися, коли буде скинуто невідкриту помилку. Якщо це не спрацює, попросіть суперпользователя або веб-майстрів (див. Внизу сторінки)
Аарон Дігулла,

мав 2 функції, названі однаково !! ДОХ!
jharris8567

16

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

Я знайшов корисні для цього деякі новіші інструменти для налагодження Chrome.

Удари Performance tab, переконайсяJavascript samples вони включені, і ви отримаєте щось подібне.

Цілком очевидно, де тут перелив! Якщо ви натиснете на нього, extendObjectви зможете фактично побачити в коді точний номер рядка.

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

Ви також можете побачити таймінги, які можуть чи не можуть бути корисними, або червону оселедець.

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


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

У Chrome, якщо ви неодноразово виводите однакові дані, вони відображатимуться так, показуючи, де проблема чіткіше. У цьому випадку стек потрапив до 7152 кадрів, перш ніж остаточно вийшов з ладу:

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


13

У моєму випадку я перетворював великий байтовий масив у рядок, використовуючи наступне:

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

bytes містив кілька мільйонів записів, що занадто велико, щоб вмістити його в стек.


У мене теж були ті ж проблеми з тією ж лінією !! thanx
ksh

так що найкраще рішення для перетворення великого байтового масиву ??
ksh

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

У моєму випадку подія клацання поширюється на дочірньому елементі. Отже, мені довелося сказати наступне:

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, це дасть вам функції в стеку викликів та направить вас до рекурсії, яка викликає помилку.


5

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

якщо ви хочете маніпулювати елементами dom і перемальовувати, використовуйте анімацію http://creativejs.com/resources/requestanimationframe/


3

Майже в кожній відповіді тут сказано, що це може бути викликане лише нескінченним циклом. Це неправда, в іншому випадку ви могли би запустити стек через глибоко вкладені дзвінки (не сказати, що це ефективно, але це, безумовно, у царині можливого). Якщо у вас є контроль над JavaScript VM, ви можете налаштувати розмір стека. Наприклад:

node --stack-size=2000

Дивіться також: Як я можу збільшити максимальний розмір стека викликів у Node.js


2

У моєму випадку два мода jQuery показували складені один на одного. Запобігання тому вирішило мою проблему.


2

Нещодавно ми додали поле на сайт адміністратора, над яким ми працюємо - contact_type ... просто так? Добре, якщо ви зателефонуєте вибору "type" і спробуєте надіслати це через jquery ajax call, це не вдається з цією помилкою, похованою в 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.
Mathias Lykkegaard Lorenzen

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.


Куди ви додали click ()
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

У мене була ця помилка, оскільки у мене були дві JS-функції з тим самим іменем


0

Якщо ви працюєте з картами google, то перевірте, чи не передається лат lng 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 отримала значення.


Сам я згорів подібним чином, в моєму випадку це було тому, що я набрав ім'я змінної ... фактично те саме.
користувач2366842

0

Натрапивши на той самий випуск, не змогли зрозуміти, у чому справа, почавши звинувачувати Бабеля;)

Маючи код, який не повертає жодного винятку в браузерах:

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

випуск був погано створений || (або) частина як 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 @ react-navigation / native --save)

Максимальний розмір стека викликів перевищив помилку

для цього я намагаюся

кеш sudo npm clean --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.