Ошибка превышения максимального размера стека вызовов
Я использую файл библиотеки JavaScript Direct Web Remoting (DWR) и получаю сообщение об ошибке только в Safari (для настольных ПК и iPad)
Это говорит
Превышен максимальный размер стека вызовов.
Что именно означает эта ошибка и останавливает ли она обработку полностью?
Также любое исправление для Safari
браузер (на самом деле на iPad Safari
это говорит
JS: превышено время ожидания выполнения
я предполагаю, что это та же проблема стека вызовов)
41 ответ
Это означает, что где-то в вашем коде вы вызываете функцию, которая, в свою очередь, вызывает другую функцию и так далее, пока не достигнете предела стека вызовов.
Это почти всегда из-за рекурсивной функции с базовым случаем, который не выполняется.
Просмотр стека
Рассмотрим этот код...
(function a() {
a();
})();
Вот стек после нескольких вызовов...
Как видите, стек вызовов растет до тех пор, пока не достигнет предела: размер стека в жестком коде браузера или исчерпание памяти.
Чтобы исправить это, убедитесь, что у вашей рекурсивной функции есть базовый случай, который можно встретить...
(function a(x) {
// The following condition
// is the base case.
if ( ! x) {
return;
}
a(--x);
})(10);
В моем случае я отправлял элементы ввода вместо их значений:
$.post( '',{ registerName: $('#registerName') } )
Вместо:
$.post( '',{ registerName: $('#registerName').val() } )
Это заморозило мою вкладку Chrome до такой степени, что даже показывало диалоговое окно "Ждать / убивать", когда страница перестает отвечать на запросы...
Иногда это можно получить, если вы случайно импортировали / внедрили один и тот же JS-файл дважды, что стоит проверить на вкладке ресурсов инспектора:)
Где-то в вашем коде есть рекурсивный цикл (т.е. функция, которая в конечном итоге вызывает себя снова и снова, пока стек не заполнится).
Другие браузеры либо имеют большие стеки (так что вместо этого вы получаете тайм-аут), либо они по какой-то причине проглатывают ошибку (может быть, неправильно размещенный try-catch).
Используйте отладчик для проверки стека вызовов при возникновении ошибки.
Проблема с обнаружением стекового потока иногда заключается в том, что трассировка стека раскручивается, и вы не сможете увидеть, что на самом деле происходит.
Я нашел некоторые из новых инструментов отладки Chrome полезными для этого.
Ударь Performance tab
, удостовериться Javascript samples
включены, и вы получите что-то вроде этого.
Это довольно очевидно, где переполнение здесь! Если вы нажмете на extendObject
вы сможете увидеть точный номер строки в коде.
Вы также можете увидеть время, которое может или не может быть полезным, или красная сельдь.
Другой полезный трюк, если вы не можете найти проблему, это поставить много console.log
заявления, где вы думаете, что проблема. Предыдущий шаг выше может помочь вам в этом.
В Chrome, если вы неоднократно выводите идентичные данные, он будет отображаться так, как показано, где проблема более ясна. В этом случае стек достиг 7152 фреймов, прежде чем окончательно рухнул:
В моем случае я преобразовывал большой байтовый массив в строку, используя следующее:
String.fromCharCode.apply(null, new Uint16Array(bytes))
bytes
содержал несколько миллионов записей, что слишком велико для размещения в стеке.
Это также может вызвать Maximum call stack size exceeded
ошибка:
var items = [];
[].push.apply(items, new Array(1000000)); //Bad
Тоже самое:
items.push(...new Array(1000000)); //Bad
Но будьте осторожны: при использовании применять этот способ, вы рискуете превысить ограничение длины аргумента движка 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]);
}
В моем случае событие 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>
В моем случае это было то, что у меня есть 2 переменные с одинаковыми именами!
В моем случае я в основном забываю получить value
из input
.
Неправильно
let name=document.getElementById('name');
param={"name":name}
Верный
let name=document.getElementById('name').value;
param={"name":name}
Если по какой-то причине вам нужен бесконечный процесс / рекурсия, вы можете использовать веб-работника в отдельном потоке. http://www.html5rocks.com/en/tutorials/workers/basics/
если вы хотите манипулировать элементами dom и перерисовывать, используйте анимацию http://creativejs.com/resources/requestanimationframe/
Проверьте подробности ошибки в консоли панели инструментов Chrome dev, это даст вам функции в стеке вызовов и проведет вас к рекурсии, которая вызывает ошибку.
Отправка элементов ввода вместо их значений, скорее всего, разрешит его, как упоминал FK
Почти каждый ответ здесь утверждает, что это может быть вызвано только бесконечным циклом. Это неправда, в противном случае вы могли бы перегрузить стек через глубоко вложенные вызовы (не говоря уже о том, что это эффективно, но, безусловно, это возможно). Если у вас есть контроль над вашей виртуальной машиной JavaScript, вы можете настроить размер стека. Например:
node --stack-size=2000
Смотрите также: Как я могу увеличить максимальный размер стека вызовов в Node.js
Недавно мы добавили поле на сайт администратора, над которым мы работаем - 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 соответственно - проблема решена.
Проверьте, есть ли у вас функция, которая вызывает сама себя. Например
export default class DateUtils {
static now = (): Date => {
return DateUtils.now()
}
}
В моем случае два мода jQuery показывали сложенными друг на друга. Предотвращение, которое решило мою проблему.
Для меня, новичка в TypeScript, проблема заключалась в геттере и сеттере _var1.
class Point2{
constructor(private _var1?: number, private y?: number){}
set var1(num: number){
this._var1 = num // problem was here, it was this.var1 = num
}
get var1(){
return this._var1 // this was return this.var1
}
}
dtTable.dataTable({
sDom: "<'row'<'col-sm-6'l><'col-sm-6'f>r>t<'row'<'col-sm-6'i><'col-sm-6'p>>",
"processing": true,
"serverSide": true,
"order": [[6, "desc"]],
"columnDefs": [
{className: "text-right", "targets": [2, 3, 4, 5]}
],
"ajax": {
"url": "/dt",
"data": function (d) {
d.loanRef = loanRef;
}
},
"fnRowCallback": function (nRow, aData, iDisplayIndex, iDisplayIndexFull) {
var editButton = '';
// The number of columns to display in the datatable
var cols = 8;
// Th row element's ID
var id = aData[(cols - 1)];
}
});
в
data
функция выше, я использовал то же имя
d.loanRef = loanRef
но не создал переменную, поэтому рекурсивно объявляет себя.
Решение: объявить
loanRef
переменной или, еще лучше, используйте другое имя, отличное от того, которое использовалось в
d.loanRef
.
Оба вызова идентичного кода ниже, если его уменьшить на 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);
Проблема в моем случае заключается в том, что у меня есть дочерний маршрут с тем же путем, что и родительский:
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' },
]
}
];
Проблема может быть связана с рекурсивными вызовами без какого-либо базового условия для его завершения.
Как и в моем случае, если вы видите приведенный ниже код, у меня было одно и то же имя для метода вызова API и метода, который я использовал для выполнения операций после этого вызова API.
const getData = async () => {
try {
const response = await getData(props.convID);
console.log("response", response);
} catch (err) {
console.log("****Error****", err);
}
};
Итак, в основном решение состоит в том, чтобы удалить этот рекурсивный вызов.
Я столкнулся с той же проблемой, я решил ее, удалив имя поля, которое использовалось дважды на AJAX, например
jQuery.ajax({
url : '/search-result',
data : {
searchField : searchField,
searchFieldValue : searchField,
nid : nid,
indexName : indexName,
indexType : indexType
},
.....
Я также столкнулся с подобной проблемой, вот детали при загрузке логотипа с помощью выпадающего окна загрузки логотипа
<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.
В моем случае я получаю эту ошибку при вызове ajax, и данные, которые я пытался передать этой переменной, не определены, то есть показывает эту ошибку, но не описывает эту переменную, не определенную. Я добавил определил, что переменная n получила значение.
Удивительно, но никто не упомянул вызов await внутри асинхронной функции. В моем случае у меня есть файлы размером более 1,5 МБ с циклами и взаимодействиями с базой данных.
async function uploadtomongodb() {
await find('user', 'user2', myobj0).then(result => {
}
})
Вы можете найти свою рекурсивную функцию в браузере crome, нажмите Ctrl+ Shift+ J, а затем вкладку исходного кода, которая дает вам процесс компиляции кода, и вы можете найти, используя точку останова в коде.
Иногда это происходит из-за типа данных для преобразования, например, у вас есть объект, который вы рассматриваете как строку.
socket.id в nodejs либо в js клиенте, например, не является строкой. чтобы использовать его как строку, вы должны добавить слово String перед:
String(socket.id);
Если вы работаете с Google Maps, то проверьте, передаются ли последние new google.maps.LatLng
имеют правильный формат. В моем случае они передавались как неопределенные.
Я использую Devexpress Scheduler в Angular, и проблема заключается в неправильном использовании переменной, назначенной тегу источника данных планировщика.