web-dev-qa-db-fra.com

"RangeError: La taille maximale de la pile d'appels a été dépassée" Pourquoi?

Si je cours 

Array.apply(null, new Array(1000000)).map(Math.random);

sur Chrome 33, je reçois

RangeError: Maximum call stack size exceeded

Pourquoi?

61

Les navigateurs ne peuvent pas gérer autant d'arguments. Voir cet extrait par exemple:

alert.apply(window, new Array(1000000000));

Cela donne RangeError: Maximum call stack size exceeded qui est le même que dans votre problème.

Pour résoudre cela, faites:

var arr = [];
for(var i = 0; i < 1000000; i++){
    arr.Push(Math.random());
}
68

Ici, il échoue à Array.apply(null, new Array(1000000)) et non à l'appel .map.

Tous les arguments de fonctions doivent tenir sur la pile d'appels (au moins les pointeurs de chaque argument). Il y a donc trop d'arguments pour la pile d'appels.

Vous devez comprendre ce qui est pile d'appels .

Stack est une structure de données LIFO, qui ressemble à un tableau ne prenant en charge que les méthodes Push et Pop.

Laissez-moi vous expliquer comment cela fonctionne par un exemple simple:

function a(var1, var2) {
    var3 = 3;
    b(5, 6);
    c(var1, var2);
}
function b(var5, var6) {
    c(7, 8);
}
function c(var7, var8) {
}

Quand ici la fonction a est appelée, elle appelle b et c. Lorsque vous appelez b et c, les variables locales de a n'y sont pas accessibles à cause des rôles étendus de Javascript, mais le moteur Javascript doit garder en mémoire les variables et les arguments locaux. Poussez-les dans la pile d'appels. Supposons que vous implémentez un moteur JavaScript avec le langage Javascript tel que Narcissus .

Nous implémentons le callStack en tant que tableau:

var callStack = [];

Chaque fois qu'une fonction appelée nous insérons les variables locales dans la pile:

callStack.Push(currentLocalVaraibles);

Une fois l'appel de fonction terminé (comme dans a, nous avons appelé b, b est terminé et nous devons revenir à a), nous récupérons les variables locales. en ouvrant la pile:

currentLocalVaraibles = callStack.pop();

Ainsi, lorsque vous vous trouvez dans a, nous souhaitons appeler à nouveau c. Insérez les variables locales dans la pile. Maintenant, comme vous le savez, pour être efficaces, les compilateurs définissent certaines limites. Ici, lorsque vous exécuterez Array.apply(null, new Array(1000000)), votre objet currentLocalVariables sera énorme car il contiendra des variables 1000000. Puisque .apply passera chacun des éléments de tableau donnés en tant qu'argument de la fonction. Une fois poussé vers la pile d'appels, cela dépassera la limite de mémoire de la pile d'appels et cette erreur sera renvoyée.

La même erreur se produit lors d'une récursion infinie (function a() { a() }) trop de fois, des éléments ont été placés dans la pile d'appels.

Notez que je ne suis pas un ingénieur compilateur et qu'il ne s'agit que d'une représentation simplifiée de ce qui se passe. C'est vraiment plus complexe que cela. Généralement, ce qui est poussé vers callstack est appelé stack frame qui contient les arguments, les variables locales et l’adresse de la fonction.

29

La réponse avec for est correcte, mais si vous voulez vraiment utiliser un style fonctionnel en évitant l'instruction for, vous pouvez utiliser ce qui suit au lieu de votre expression:

Array.from (Array (1000000), () => Math.random ());

La méthode Array.from () crée une nouvelle instance Array à partir d'un objet de type tableau ou itératif. Le deuxième argument de cette méthode est une fonction de la carte permettant d’appeler tous les éléments du tableau.

En suivant la même idée, vous pouvez le réécrire avec Opérateur ES2015 Spread :

[... Array (1000000)]. Map (() => Math.random ())

Dans les deux exemples, vous pouvez obtenir un index de l'itération si vous avez besoin, par exemple:

[... Array (1000000)]. Map ((_, i) => i + Math.random ())

1
Alexander

Vous devez d'abord comprendre Call Stack. Comprendre la pile d’appels vous expliquera également comment fonctionne la «hiérarchie des fonctions et l’ordre d’exécution» dans JavaScript Engine. 

La pile d'appels est principalement utilisée pour l'appel de fonction (call). Comme la pile d'appels est unique, l'exécution de la ou des fonction (s) est effectuée, une à la fois, de haut en bas. Cela signifie que la pile d'appels est synchrone. Lorsque vous entrez dans une fonction, une entrée correspondant à cette fonction est insérée dans la pile d'appels et lorsque vous quittez cette fonction, cette même entrée est extraite de la pile d'appels. Donc, si tout se passe bien, au début et à la fin, Call Stack sera vide.

Voici l'illustration de Call Stack:  enter image description here

Maintenant, si vous fournissez trop d'arguments ou si vous êtes pris dans un appel récursif non géré. Vous allez rencontrer 

RangeError: taille maximale de la pile d'appels dépassée

ce qui est assez évident comme expliqué par d'autres .  enter image description here  enter image description here

J'espère que cela t'aides !

0
Om Prakash Sao