Erreur de taille maximale de la pile d'appels dépassée

533

J'utilise un fichier de bibliothèque JavaScript Direct Web Remoting (DWR) et j'obtiens une erreur uniquement dans Safari (ordinateur de bureau et iPad)

Ça dit

Taille de la pile d'appel maximale dépassée.

Que signifie exactement cette erreur et arrête-t-elle complètement le traitement?

Aussi tout correctif pour le Safarinavigateur (en fait sur le iPad Safari, dit-il

JS: délai d'exécution dépassé

que je suppose est le même problème de pile d'appels)

testndtv
la source
2
j'ai eu cette erreur en essayant d'envoyer des variables (sans les déclarer), via dataajax. Correction de l'erreur en déclarant les variables.
phpfresher

Réponses:

621

Cela signifie que quelque part dans votre code, vous appelez une fonction qui à son tour appelle une autre fonction et ainsi de suite, jusqu'à ce que vous atteigniez la limite de pile d'appels.

Cela est presque toujours dû à une fonction récursive avec un cas de base qui n'est pas respecté.

Affichage de la pile

Considérez ce code ...

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

Voici la pile après une poignée d'appels ...

Inspecteur Web

Comme vous pouvez le voir, la pile d'appels augmente jusqu'à ce qu'elle atteigne une limite: la taille de pile codée en dur du navigateur ou l'épuisement de la mémoire.

Afin de le corriger, assurez-vous que votre fonction récursive a un cas de base qui peut être respecté ...

(function a(x) {
    // The following condition 
    // is the base case.
    if ( ! x) {
        return;
    }
    a(--x);
})(10);
alex
la source
6
Thx beaucoup pour ça..Alors y a-t-il un moyen / outil par lequel je peux que la limite de pile soit épuisée..Encore une fois, car c'est un énorme fichier de bibliothèque et non créé par moi, je ne sais pas complètement où les choses pourraient mal tourner ..
testndtv
52
@Oliver Si vous devez exécuter une fonction près de 300 billions de fois, vous faites probablement quelque chose de mal.
ceejayoz
3
@Oliver - vous voudrez peut-être vous pencher sur la programmation dynamique - je suppose que vous n'avez pas autant de solutions uniques, vous répétez des étapes, vous devrez donc peut-être la programmer en temps polynumial plutôt qu'en quadratique. en.wikipedia.org/wiki/Dynamic_programming
newshorts
4
@Akhoy Dans une fonction régulière oui, mais pensez à une fonction récursive. Il en appelle un autre (lui-même) et laisse l'adresse de retour du premier sur la pile (sinon où le PC saurait-il où aller?). Cela s'appelle bien sûr à nouveau, chaque fois en poussant une adresse de retour sur la pile. Lorsque cela s'enfuit en raison d'un scénario de base qui ne sera probablement pas atteint au siècle prochain, vous obtenez un débordement de pile.
alex
5
Votre code exécute ce même segment inefficace 134217728 fois. Sans blague, c'est lent. Vous devez utiliser des opérateurs au niveau du bit afin de pouvoir dérouler les 9 niveaux de boucles en un seul niveau de boucles.
Jack Giffin
82

Vous pouvez parfois l'obtenir si vous importez / intégrez accidentellement le même fichier JavaScript deux fois, cela vaut la peine de vérifier dans l'onglet ressources de l'inspecteur.

lucygenik
la source
9
Ce problème se produit généralement lorsque vous importez / incorporez accidentellement le même fichier JS deux fois. Le processus exact qui provoque la boucle récursive n'est pas quelque chose sur lequel je tiens à spéculer, mais je suppose que c'est quelque chose comme conduire deux voitures identiques à 100 mph via la même passerelle de péage.
lucygenik
3
Je ne pense pas que ce soit le cas. Si deux mêmes types de fonctions ou de variables sont incorporées, ces dernières remplaceront les définitions précédentes.
ssudaraka
Ouais ... Je ne sais pas vraiment pourquoi cela se produit ou quel est le processus, j'aurais bien que cela l'emporterait aussi.
lucygenik
Un peu tard pour la fête, mais imaginerait que c'est lorsque le code est en dehors de toute fonction, les deux seraient exécutés dans les deux fichiers JS, et l'un ne se remplacerait probablement pas car ils sont en dehors de toute fonction.
Cillian Collins
1
@lucygenik la prochaine fois que vous voudrez être plus prudent lors de l'approbation des modifications de troll sur vos messages. Ce message a presque été supprimé comme spam (consultez l'historique)
Jean-François Fabre
57

Dans mon cas, j'envoyais des éléments d'entrée au lieu de leurs valeurs:

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

Au lieu de:

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

Cela a gelé mon onglet Chrome au point qu'il ne m'a même pas montré la boîte de dialogue `` Attendre / Tuer '' lorsque la page ne répondait plus ...

FKasa
la source
3
Je me demande pourquoi il n'y a pas d'exception pour une telle erreur ... Merci!
Džuris
Merci beaucoup, cela m'a sauvé
Eme Hado
J'étais sur le point d'ajouter ceci comme solution possible. Je viens de faire cette chose exacte lol
Michael Buchok
31

Il y a une boucle récursive quelque part dans votre code (c'est-à-dire une fonction qui finit par s'appeler encore et encore jusqu'à ce que la pile soit pleine).

Les autres navigateurs ont des piles plus importantes (vous obtenez donc un délai d'attente à la place) ou ils avalent l'erreur pour une raison quelconque (peut-être un try-catch mal placé).

Utilisez le débogueur pour vérifier la pile d'appels lorsque l'erreur se produit.

Aaron Digulla
la source
Merci beaucoup pour votre réponse. Sur IE / FF, le code semble fonctionner correctement ... Seulement sur Safari de bureau et iPad Safari, j'obtiens l'erreur. En fait, le fichier JS n'est pas ma propre création, mais est un fichier de bibliothèque (de DWR engine.js) .. directwebremoting.org Aussi lorsque vous dites "débogueur pour vérifier la pile d'appels", comment puis-je faire cela en utilisant l'inspecteur Safari?
testndtv
Je n'ai aucune expérience avec Safari Inspector. Essayez d'ouvrir le débogueur JavaScript et chargez votre page. Le débogueur doit s'arrêter lorsqu'une erreur non enregistrée est levée. Si cela ne fonctionne pas, demandez au superutilisateur ou aux webmasters (voir en bas de la page)
Aaron Digulla
avait 2 fonctions nommées la même chose !! DOH!
jharris8567
16

Le problème avec la détection des débordements de pile est parfois que la trace de la pile se déroule et vous ne pourrez pas voir ce qui se passe réellement.

J'ai trouvé certains des nouveaux outils de débogage de Chrome utiles pour cela.

Appuyez sur Performance tab, assurez-vous qu'ils Javascript samplessont activés et vous obtiendrez quelque chose comme ça.

Il est assez évident où le débordement est là! Si vous cliquez sur, extendObjectvous pourrez réellement voir le numéro de ligne exact dans le code.

entrez la description de l'image ici

Vous pouvez également voir les horaires qui peuvent ou non être utiles ou un hareng rouge.

entrez la description de l'image ici


Une autre astuce utile si vous ne pouvez pas réellement trouver le problème est de mettre beaucoup de console.logdéclarations là où vous pensez que le problème est. L'étape précédente ci-dessus peut vous aider.

Dans Chrome, si vous produisez à plusieurs reprises des données identiques, il les affichera comme ceci, montrant où le problème est plus clair. Dans ce cas, la pile a atteint 7152 images avant de se bloquer définitivement:

entrez la description de l'image ici

Simon_Weaver
la source
13

Dans mon cas, je convertissais un grand tableau d'octets en une chaîne en utilisant ce qui suit:

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

bytes contenait plusieurs millions d'entrées, ce qui est trop gros pour tenir dans la pile.

Nate Glenn
la source
Moi aussi j'ai eu le même problème avec la même ligne !! merci
ksh
alors quelle est la meilleure solution pour convertir un grand tableau d'octets ??
ksh
6
@KarthikHande Vous devez le faire dans une boucle for au lieu d'un seul appel. var uintArray = new Uint16Array(bytes); var converted = []; uintArray.forEach(function(byte) {converted.push(String.fromCharCode(byte))});
Nate Glenn
comment convertir cela en JSON? J'ai essayé JSON.parse mais cela n'a pas fonctionné ... J'utilise Uintl8Array
ksh
@KarthikHande Je ne peux pas dire sans voir tout le problème. Veuillez ouvrir une autre question avec tous les détails.
Nate Glenn
6

Dans mon cas, l'événement click se propageait sur l'élément enfant. Donc, j'ai dû mettre ce qui suit:

e.stopPropagation ()

sur événement de clic:

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

Voici le code html:

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

Vérifiez les détails de l'erreur dans la console de la barre d'outils de développement Chrome, cela vous donnera les fonctions de la pile d'appels et vous guidera vers la récursivité qui cause l'erreur.

chim
la source
3

Presque toutes les réponses ici indiquent que cela ne peut être causé que par une boucle infinie. Ce n'est pas vrai, sinon vous pourriez dépasser la pile par des appels profondément imbriqués (pour ne pas dire que c'est efficace, mais c'est certainement dans le domaine du possible). Si vous contrôlez votre machine virtuelle JavaScript, vous pouvez ajuster la taille de la pile. Par exemple:

node --stack-size=2000

Voir aussi: Comment puis-je augmenter la taille maximale de la pile d'appels dans Node.js

ghayes
la source
2

Dans mon cas, deux modaux jQuery montraient empilés les uns sur les autres. Empêcher cela a résolu mon problème.

Barry Guvenkaya
la source
2

Nous avons récemment ajouté un champ à un site d'administration sur lequel nous travaillons - contact_type ... c'est facile, non? Eh bien, si vous appelez le "type" sélectionné et essayez d'envoyer cela via un appel ajqu jquery, il échoue avec cette erreur enfouie profondément dans jquery.js Ne faites pas ceci:

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

Le problème est ce type: type - je crois que c'est nous qui nommons l'argument "type" - avoir une variable de valeur nommée type n'est pas le problème. Nous avons changé cela en:

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

Et réécrit some_function.php en conséquence - problème résolu.

Scott
la source
1

Vérifiez si vous avez une fonction qui s'appelle elle-même. Par exemple

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

Cela peut également provoquer une Maximum call stack size exceedederreur:

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

Pareil ici:

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

Depuis les documents Mozilla :

Mais attention: en utilisant Apply de cette façon, vous courez le risque de dépasser la limite de longueur d'argument du moteur JavaScript. Les conséquences de l'application d'une fonction avec trop d'arguments (pensez à plus de dizaines de milliers d'arguments) varient selon les moteurs (JavaScriptCore a une limite d'arguments codée en dur de 65536), car la limite (en fait même la nature de toute pile trop grande) comportement) n'est pas spécifié. Certains moteurs lèveront une exception. Plus pernicieusement, d'autres limiteront arbitrairement le nombre d'arguments réellement passés à la fonction appliquée. Pour illustrer ce dernier cas: si un tel moteur avait une limite de quatre arguments (les limites réelles sont bien sûr significativement plus élevées), ce serait comme si les arguments 5, 6, 2, 3 avaient été passés pour s'appliquer dans les exemples ci-dessus, plutôt que le tableau complet.

Alors essayez:

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

Les deux invocations du code identique ci-dessous si elles sont diminuées de 1 fonctionnent dans Chrome 32 sur mon ordinateur, par exemple 17905 vs 17904. Si elles sont exécutées telles quelles, elles produiront l'erreur "RangeError: la taille maximale de la pile d'appels est dépassée". Il semble que cette limite ne soit pas codée en dur mais dépend du matériel de votre machine. Il semble que s'il est invoqué en tant que fonction, cette limite auto-imposée est plus élevée que s'il est invoqué en tant que méthode, c'est-à-dire que ce code particulier utilise moins de mémoire lorsqu'il est invoqué en tant que fonction.

Appelé comme méthode:

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

ninja.chirp(17905);

Appelé en tant que fonction:

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

chirp(20889);
Elijah Lynn
la source
0

vous pouvez trouver votre fonction récursive dans le navigateur crome, appuyez sur ctrl + shift + j puis sur l'onglet source, ce qui vous donne le flux de compilation du code et vous pouvez le trouver en utilisant le point d'arrêt dans le code.

Vishal Patel
la source
Parfois, cependant, il peut être difficile de déterminer l'origine de l'erreur. Nous avons BEAUCOUP de JavaScript sur notre site.
Mathias Lykkegaard Lorenzen
0

J'ai également rencontré un problème similaire ici: les détails lors du téléchargement du logo à l'aide de la zone de téléchargement du logo déroulant

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

avant correction mon code était:

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

L'erreur dans la console:

entrez la description de l'image ici

Je l'ai résolu en supprimant onclick="$('#filePhoto').click()"de la balise div.

spacedev
la source
Où avez-vous ajouté le clic ()
Tsea
0

J'étais confronté au même problème que je l'ai résolu en supprimant un nom de champ qui a été utilisé deux fois sur ajax, par exemple

    jQuery.ajax({
    url : '/search-result',
    data : {
      searchField : searchField,
      searchFieldValue : searchField,
      nid    :  nid,
      indexName : indexName,
      indexType : indexType
    },
.....
Tajdar Khan Afridi
la source
0

Je sais que ce fil est ancien, mais je pense qu'il vaut la peine de mentionner le scénario où j'ai trouvé ce problème afin qu'il puisse aider les autres.

Supposons que vous ayez des éléments imbriqués comme ceci:

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

Vous ne pouvez pas manipuler les événements d'élément enfant à l'intérieur de l'événement de son parent car il se propage à lui-même, effectuant des appels récursifs jusqu'à ce que l'exception soit levée.

Donc, ce code échouera:

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

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

Vous avez deux options pour éviter cela:

  • Déplacez l'enfant vers l'extérieur du parent.
  • Appliquez la fonction stopPropagation à l'élément enfant.
Weslley Ramos
la source
0

J'ai eu cette erreur car j'avais deux fonctions JS avec le même nom

Dragon de glace
la source
0

Si vous travaillez avec google maps, vérifiez si le dernier lng est passé dans new google.maps.LatLngun format correct. Dans mon cas, ils étaient passés comme indéfinis.

User-8017771
la source
0

Le problème dans mon cas est que j'ai des enfants qui ont le même chemin avec le parent:

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

J'ai donc dû supprimer la ligne de la route des enfants

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

dans mon cas, j'obtiens cette erreur lors d'un appel ajax et les données que j'ai essayé de transmettre ne sont pas définies, cela me montre cette erreur mais ne décrit pas cette variable non définie. J'ai ajouté défini que la variable n a une valeur.

asifaftab87
la source
m'a brûlé de la même manière, dans mon cas, c'est parce que j'ai tapé un nom de variable ... en fait la même chose.
user2366842
0

Je suis tombé sur le même problème, je n'ai pas compris ce qui n'allait pas, j'ai commencé à blâmer Babel;)

Avoir du code ne renvoyant aucune exception dans les navigateurs:

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

problème a été mal créé || (ou) partie lorsque babel crée sa propre vérification de type:

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

donc enlever

|| null

fait fonctionner la transpilation de babel.

Raphael
la source
0

Dans mon cas, par erreur, j'ai attribué le même nom de variable et à la fonction val "class_routine_id"

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

ça devrait être comme:

 var class_routine_id = $("#class_routine_id").val(); 
Alok Jha
la source
0

J'utilise React-Native 0.61.5 avec ( npm 6.9.0 & node 10.16.1 )

Pendant que j'installe de nouvelles bibliothèques dans Project, j'ai obtenu un des

(par exemple, npm install @ react-navigation / native --save)

Erreur de taille maximale de la pile d'appels dépassée

pour ça j'essaye

sudo npm cache clean --force

(Remarque: - La commande ci-dessous prend généralement 1 à 2 minutes en fonction de la taille de votre cache npm )

DevAelius
la source
-1

Parfois arrivé à cause du type de données converti, par exemple vous avez un objet que vous considérez comme une chaîne.

socket.id dans nodejs soit dans js client comme exemple, n'est pas une chaîne. pour l'utiliser comme chaîne, vous devez ajouter le mot chaîne avant:

String(socket.id);
Hussein mahyoub
la source
-1

J'essayais d'assigner une variable, une valeur, alors que cette variable n'avait pas été déclarée.

La déclaration de la variable a corrigé mon erreur.

Phillip Quinlan
la source