82

If I run

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

on Chrome 33, I get

RangeError: Maximum call stack size exceeded

Why?

Farid Nouri Neshat's user avatar
Farid Nouri Neshat
30.5k6 gold badges80 silver badges128 bronze badges
askedMar 2, 2014 at 4:15
Fabrizio Giordano's user avatar
5
  • What are you actually wanting to do? Fill an array with 1000000 random numbers? Or did you have something else in mind because ofArray.apply?CommentedMar 2, 2014 at 4:22
  • Yes, I am creating an array of 1,000,000 random numbers. I am using Function.prototype.apply because it doesn't ignore holes.CommentedMar 2, 2014 at 4:24
  • 3
    Well, you are exceeding the browsers maximum number of supportedarguments doing it this way. (normally ~65536). Afor loop would probably be more sensible.CommentedMar 2, 2014 at 4:27
  • 1
    If you are absolutely determined not to use afor loop and really want to usemap then you could use this much slower method (at least I would expect it to be)Object.keys([].concat(Array(10000001).join().split(''))).map(Math.random)CommentedMar 2, 2014 at 4:37
  • I wrote a small test:console.time('object'); var arr = Object.keys([].concat(Array(1000001).join().split(''))).map(Math.random) console.timeEnd('object'); console.time('loop'); var arr = []; var i = 1000000, while(i--){ arr.push(Math.random()); } console.timeEnd('loop'); Object is 2x time faster.CommentedMar 2, 2014 at 5:01

5 Answers5

89

Browsers can't handle that many arguments. See this snippet for example:

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

This yieldsRangeError: Maximum call stack size exceeded which is the same as in your problem.

To solve that, do:

var arr = [];for(var i = 0; i < 1000000; i++){    arr.push(Math.random());}
answeredMar 2, 2014 at 4:32
Derek 朕會功夫's user avatar
Sign up to request clarification or add additional context in comments.

Comments

36

Here it fails atArray.apply(null, new Array(1000000)) and not the.map call.

All functions arguments must fit on callstack(at least pointers of each argument), so in this they are too many arguments for the callstack.

You need to the understand what iscall stack.

Stack is a LIFO data structure, which is like an array that only supports push and pop methods.

Let me explain how it works by a simple example:

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

When here functiona is called, it will callb andc. Whenb andc are called, the local variables ofa are not accessible there because of scoping roles of Javascript, but the Javascript engine must remember the local variables and arguments, so it will push them into the callstack. Let's say you are implementing a JavaScript engine with the Javascript language likeNarcissus.

We implement the callStack as array:

var callStack = [];

Everytime a function called we push the local variables into the stack:

callStack.push(currentLocalVaraibles);

Once the function call is finished(like ina, we have calledb,b is finished executing and we must return toa), we get back the local variables by poping the stack:

currentLocalVaraibles = callStack.pop();

So when ina we want to callc again, push the local variables in the stack. Now as you know, compilers to be efficient define some limits. Here when you are doingArray.apply(null, new Array(1000000)), yourcurrentLocalVariables object will be huge because it will have1000000 variables inside. Since.apply will pass each of the given array element as an argument to the function. Once pushed to the call stack this will exceed the memory limit of call stack and it will throw that error.

Same error happens on infinite recursion(function a() { a() }) as too many times, stuff has been pushed to the call stack.

Note that I'm not a compiler engineer and this is just a simplified representation of what's going on. It really is more complex than this. Generally what is pushed to callstack is calledstack frame which contains the arguments, local variables and the function address.

answeredMar 2, 2014 at 4:46
Farid Nouri Neshat's user avatar

3 Comments

Thank you for explaining this. I have been struggling to understand why I get this same exception in a method which ironically is recursive, but it occurs with only 1 level deep. Turns out, it wasn't a recursion problem.Array.prototype.push.apply was being used and passes an extremely large argument list. It never occured to me that the argument list to the function was too large. I was convinced it was a recursion problem. Thank you again!
Your explanation of stack in JS is half-wrong. In JS functionsb() andc() WILL have access to local variables of functiona()! This is called closures, and this is very important feature of JS. But in general you are explaining concept of stack right way
No closures in this example, I didn't want to make it more complicated.
18

You first need to understand Call Stack. Understanding Call stack will also give you clarity to how "function hierarchy and execution order" works in JavaScript Engine.

The call stack is primarily used for function invocation (call). Since there is only one call stack. Hence, all function(s) execution get pushed and popped one at a time, from top to bottom.

It means the call stack is synchronous. When you enter a function, an entry for that function is pushed onto the Call stack and when you exit from the function, that same entry is popped from the Call Stack. So, basically if everything is running smooth, then at the very beginning and at the end, Call Stack will be found empty.

Here is the illustration of Call Stack:enter image description here

Now, if you provide too many arguments or caught inside any unhandled recursive call. You will encounter

RangeError: Maximum call stack size exceeded

which is quite obvious as explained by others.enter image description hereenter image description here

Hope this helps !

answeredNov 27, 2018 at 6:27
Om Sao's user avatar

2 Comments

do you have the link or title of the video you referenced? I would like to watch if it's still available.
4

The answer withfor is correct, but if you really want to use functional style avoidingfor statement - you can use the following instead of your expression:

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

The Array.from() method creates a new Array instance from an array-like or iterable object. The second argument of this method is a map function to call on every element of the array.

Following the same idea you can rewrite it usingES2015 Spread operator:

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

In both examples you can get an index of the iteration if you need, for example:

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

answeredSep 7, 2017 at 20:19
Alexander's user avatar

1 Comment

Array(1000000).fill().map(() => Math.random()) also works
1

In my experience, this error occurred as a result of a recursive function that never terminates. So I put in a condition for which the recursion must stop executing and return (break out).So by adding the line of code below, I was able to get rid of this error.

if (debtTypesCounter === debtTypesLength) return;

So, you can tweak this idea to suit your condition. Hope this helps?

answeredAug 2, 2021 at 9:08
Joshua Gato's user avatar

Comments

Your Answer

Sign up orlog in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

By clicking “Post Your Answer”, you agree to ourterms of service and acknowledge you have read ourprivacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.