I’m trying to run npm install
, this is output from console:
npm ERR! Linux 4.8.0-27-generic
npm ERR! argv "/usr/bin/nodejs" "/usr/bin/npm" "install"
npm ERR! node v6.9.1
npm ERR! npm v3.10.8
npm ERR! Maximum call stack size exceeded
npm ERR!
npm ERR! If you need help, you may report this error at:
npm ERR! <https://github.com/npm/npm/issues>
and this is content of npm-debug.log
:
113791 verbose stack RangeError: Maximum call stack size exceeded
113791 verbose stack at Object.color (/usr/lib/node_modules/npm/node_modules/npmlog/node_modules/console-control-strings/index.js:115:32)
113791 verbose stack at EventEmitter.log._format (/usr/lib/node_modules/npm/node_modules/npmlog/log.js:252:51)
113791 verbose stack at EventEmitter.<anonymous> (/usr/lib/node_modules/npm/node_modules/npmlog/log.js:138:24)
113791 verbose stack at emitThree (events.js:116:13)
113791 verbose stack at emit (events.js:194:7)
113791 verbose stack at .<anonymous> (/usr/lib/node_modules/npm/node_modules/npmlog/node_modules/are-we-there-yet/tracker-group.js:23:18)
113791 verbose stack at emitThree (events.js:116:13)
113791 verbose stack at emit (events.js:194:7)
113791 verbose stack at .<anonymous> (/usr/lib/node_modules/npm/node_modules/npmlog/node_modules/are-we-there-yet/tracker-group.js:23:18)
113791 verbose stack at emitThree (events.js:116:13)
113791 verbose stack at emit (events.js:194:7)
113792 verbose cwd /home/giorgi/AdMove/dev/web-advertiser-admove
113793 error Linux 4.8.0-27-generic
113794 error argv "/usr/bin/nodejs" "/usr/bin/npm" "install"
113795 error node v6.9.1
113796 error npm v3.10.8
113797 error Maximum call stack size exceeded
113798 error If you need help, you may report this error at:
113798 error <https://github.com/npm/npm/issues>
113799 verbose exit [ 1, true ]
Removed node_modules
several times and tried to reinstall. Can’t understand what’s the reason that causes this and how to fix it.
asked Nov 12, 2016 at 18:41
GROX13GROX13
4,4554 gold badges23 silver badges39 bronze badges
6
metzelder’s answer helped me fix the issue. however if you run the command npm cache clean
, it will give you a message
As of npm@5, the npm cache self-heals from corruption issues and data extracted from the cache is guaranteed to be valid
So, as of npm5 you can do by adding a --force
flag to the command.
So the command is:
npm cache clean --force
G-Ram
334 silver badges6 bronze badges
answered Jul 14, 2018 at 8:06
codingbruhcodingbruh
2,2131 gold badge11 silver badges14 bronze badges
10
npm rebuild
it has solved my problem
answered Nov 7, 2018 at 6:51
YYYYYY
3,3805 gold badges19 silver badges23 bronze badges
6
Try removing package-lock.json
and the node_modules
folder:
rm package-lock.json
rm -r node_modules
bobbyz
4,8783 gold badges30 silver badges42 bronze badges
answered Apr 6, 2019 at 17:37
7
I had the same issue with npm install
.
After a lot of search, I found out that removing your .npmrc
file or its content (found at %USERPROFILE%/.npmrc
), will solve this issue. This worked for me.
Rene Knop
1,7383 gold badges14 silver badges27 bronze badges
answered Nov 19, 2018 at 19:37
Arwa S kArwa S k
2963 silver badges7 bronze badges
1
npm uninstall
npm cache clean --force
I tried these two methods but they didn’t work. After, I deleted the node_modules
directory and ran npm install
again, it still didn’t work. Lastly, I deleted package-lock.json
and created a new package-lock.json
file using
npm install
Michael Mior
27.7k8 gold badges88 silver badges112 bronze badges
answered Feb 18, 2019 at 11:24
Kerim KuşcuKerim Kuşcu
4074 silver badges12 bronze badges
4
I have overcome this issue by doing following:
-
Delete all the content of the npm dependencies. You can find the default install location according to this thread:
https://stackoverflow.com/a/5926706/1850297 -
Before you run
npm install
command, I suggest to runnpm cache clean --force
answered Apr 10, 2018 at 9:45
metzeldermetzelder
6262 gold badges15 silver badges33 bronze badges
1
npm rebuild
will work for sure
answered Oct 12, 2019 at 3:38
Gopi PGopi P
5089 silver badges19 bronze badges
4
In my case, update to the newest version:
npm install -g npm
answered Aug 15, 2019 at 14:41
AvantContraAvantContra
3611 gold badge3 silver badges10 bronze badges
I deleted
node_modules
and then reinstalled by
npm install
It worked for me
answered Oct 27, 2018 at 15:00
Umair KhalidUmair Khalid
2,1791 gold badge19 silver badges28 bronze badges
3
I have also faced the same problem and this is how i resolved it.
-
First of all you need to make sure that your node and npm versions are up to date. if not please upgrade your node and npm packages to latest versions.
nvm install 12.18.3 // update node version through node version manager npm install npm // update your npm version to latest
-
Delete your
node_modules
folder andpackage-lock.json
file. -
Force clean the entire NPM cache by using following comand.
npm cache clean --force
-
Re-Install all the dependencies.
npm install
-
If above step didn’t resolve your problem, try to re-install your dependencies after executing following command.
npm rebuild
answered Aug 3, 2020 at 16:37
1
This issue can also happen if you’re trying to install a package that doesn’t exist or if you’re trying to install a version that doesn’t exist.
answered Dec 26, 2019 at 13:57
MelchiaMelchia
21.5k21 gold badges97 silver badges113 bronze badges
2
npm cache clean returns below message
As of npm@5, the npm cache self-heals from corruption issues and data extracted from the cache is guaranteed to be valid. If you want to make sure everything is consistent, use ‘npm cache verify’ instead. On the other hand, if you’re debugging an issue with the installer, you can use
npm install --cache /tmp/empty-cache
to use a temporary cache instead of nuking the actual one.
If you run npm cache verify
, as specified above, then it actually runs cache verification and garbage collection which fixes the problem.
Cache verified and compressed (~AppDataRoamingnpm-cache_cacache):
Content verified: 6183 (447214684 bytes) Content garbage-collected: 16
(653745 bytes) Index entries: 9633
MetaSean
76115 silver badges24 bronze badges
answered Mar 18, 2019 at 13:01
SynsterSynster
3298 silver badges15 bronze badges
1
Happened in docker (node:15-buster) for me.
Remember to use WORKDIR /<folder>
so that it doesn’t conflict with original npm libraries installed.
The folder can be anything but system folders, so that includes using /
.
answered Oct 30, 2020 at 8:29
danieltan95danieltan95
7646 silver badges12 bronze badges
3
In case none of these answer work for you, it may be because the terminal you’re using isn’t the right one/ your node_modules
is used by another part of your computer.
In my case I kept juggling between this error (maximum call stack size exceeded) and the access error event when I did a sudo npm i
.
The fix was to close my IDE (which was WebStorm), run npm i
in a basic terminal, and that was it.
Michael Mior
27.7k8 gold badges88 silver badges112 bronze badges
answered May 14, 2019 at 10:33
1
I’m not a Windows user, so if you are, try to check Rene Knop comment.
For Unix/OSX users, I’ve removed the root .npmrc file ~/.npmrc
.
Before you’re going to try it, please,
check if there is nothing necessary over there
you can use this command to bring all content into your terminal: cat ~/.npmrc
.
If you have got something like:
cat: /Users/$USER/.npmrc: No such file or directory
to save a copy:
cp ~/.npmrc ~/.npmrc_copy
Now, try to remove it (Works for bash users: Unix / Ubuntu / OSX …):
rm -f ~/.npmrc
This worked for me.
Hope this will be helpful for others.
answered Apr 11, 2019 at 14:17
naorznaorz
3493 silver badges6 bronze badges
In my case I had a custom .npmrc
file that included an auth token for authenticating with a private npm registry.
The token had expired, which helpfully returned code E401: Incorrect or missing password
locally, but ERR! Maximum call stack size exceeded
from the CI build.
answered Aug 25, 2021 at 12:18
wolfyukwolfyuk
7441 gold badge8 silver badges24 bronze badges
You uninstall npm package and force clean the cache and close terminal and reinstall whichever package be.
$sudo npm uninstall <package - name>
$sudo npm cache clean --force
Then restart terminal and check
Still not working upgrade both npm and node to the latest version
answered Feb 17, 2019 at 7:53
Today we encountered this error when running an npm prune
even after running an npm cache clean --force
.
Versions:
node 13.8.0
npm 6.13.6
Deleting the package-lock.json
worked for this case as well. Thank you all!
answered Apr 8, 2020 at 14:38
In general, once a module has been installed, it’s much more convenient to use npm ci
instead of npm install
. Please check out this SO answer for the advantages of the former with respect to the later in a production environment.
So please just run
npm ci
All dependencies will be updated, and the problem will disappear. Or it will error in the case there’s some grave de-synchronization between one and the other.
answered Nov 2, 2020 at 11:18
jjmerelojjmerelo
21.9k7 gold badges37 silver badges83 bronze badges
Most of the times, this issue occurs if you are using the system provided by the organization you work for and it’s vpn restricts the use of this command.
In this case, you may try to disconnect from organization vpn and then execute this command.
answered Mar 17, 2021 at 12:03
jaihindjaihind
1,0721 gold badge8 silver badges9 bronze badges
I tried everything to fix this issue on my Mac. I think the issue started when I had already downloaded npm from Node.js and then reinstalled it with Homebrew while following along with a Team Treehouse video.
Here’s what I tried:
From https://docs.npmjs.com/misc/removing-npm
sudo npm uninstall npm -g
sudo make uninstall
sudo rm -rf /usr/local/{lib/node{,/.npm,_modules},bin,share/man}/npm*
From How do I completely uninstall Node.js, and reinstall from beginning (Mac OS X)
sudo rm -rf /usr/local/{lib/node{,/.npm,_modules},bin,share/man}/{npm*,node*,man1/node*}
Here’s what worked:
In the end, the only thing that worked for me was to clone down the npm-reinstall repo from GitHub that completely removed everything related to npm on my Mac.
https://github.com/brock/node-reinstall
I then had to reinstall node and npm from Node.js.
answered Nov 6, 2018 at 13:33
0
I tried everything to fix this issue on my windows 7 machine like
Reinstalling and rebuilding npm
At last, I fixed this small configuration setting issue by wasting my entire day.
How I resolved this issue
Removing my project specific configurations in global .npmrc
at location like drive:/Windows/Users/../.npmrc
answered May 16, 2019 at 10:42
I solved it 100% I had this problem with gulp version: 3.5.6.
You should clean the package-lock.js and then run npm install
and It worked form
answered Nov 1, 2019 at 12:47
Our company dev environment uses Artifactory
as the default registry for our NPM dependencies, and when running npm install
it was defaulting to this, which did not work… so manually specifying the main npm registry via npm install --registry https://registry.npmjs.org
fixed this issue for me…
answered Jan 7, 2020 at 16:18
Joshua BarkerJoshua Barker
9072 gold badges10 silver badges22 bronze badges
I was facing the same error, I was trying to install jest into to one of the packages in a monorepo project.
If you are using Yarn + Learna to package a monorepo project, you will have to navigate to the package.json inside the target package and then run npm install
or npm install <package name>
.
answered Jun 10, 2020 at 3:58
I don’t know why, but I ran npm install
with sudo and it worked.
sudo npm install
answered Sep 27, 2021 at 15:41
SpixzSpixz
311 silver badge7 bronze badges
I had this problem and it was due to an upgrade of my git executable. I rolled back to Git-2.21.0.rc1.windows.1-64-bit and added this to my environment path and it fixed my issue.
answered Feb 15, 2019 at 23:09
Finchy70Finchy70
4219 silver badges24 bronze badges
The one thing that finally worked for me on Mac was upgrading from node 8.12 to 10.x using NVM.
I uninstalled all other versions of Node with NVM, then installed 10.x, then ran nvm alias default node
, which tells NVM to always default to the latest available node version on a shell.
After that, my live reloading issue went away!
answered Jul 30, 2019 at 18:12
bildungsromanbildungsroman
3651 gold badge8 silver badges17 bronze badges
Switching to yarn solved the issue for me.
answered Oct 17, 2019 at 20:33
vgaltesvgaltes
1,14011 silver badges17 bronze badges
Sometimes, you may see the npm maximum call stack size exceeded error when you run the npm install
command:
npm ERR! Maximum call stack size exceeded
The detail of the error may vary inside your npm-debug.log
file, but this error is commonly caused by npm unable to complete a process related to your npm install
command.
For example, if you’re updating a package version, then npm may not be able to delete the old files inside your node_modules
folder and replace it with the new ones.
The exact cause of the error may vary depending on what packages you are installing, but you can use the following steps to try and fix the error:
Installing the latest NodeJS and npm version
First, make sure that you are using the latest NodeJS and npm versions for your project.
Now the npm install
command sometimes builds native addons for NodeJS which compiles a native program written in C or C++ and uses it as a part of your Node package.
When you update your NodeJS and npm to the latest version, the compiled native addons inside your node_modules/
folder must be rebuild using the latest npm version for it to work.
If you don’t want to run npm install
every time you change the NodeJS version, then I recommend you to use Node Version Manager to install multiple NodeJS versions on your local computer.
Once you installed the latest NodeJS and npm software, then delete your node_modules
folder and package-lock.json
file:
rm -rf node_modules && rm package-lock.json
Once both are removed, then clean your npm cache using the following command:
Finally, try installing your dependencies again:
You should be able to install all dependencies without the maximum call stack size exceeded error now.
Sometimes, there’s another error that may appear when you run the npm install
command.
You may get a JavaScript heap out of memory error, or there’s npm code 1 error.
I hope these tutorials have helped you to debug npm installation issues. 🙏
I’m opening this issue because:
- npm is crashing.
- npm is producing an incorrect install.
- npm is doing something I don’t understand.
- Other (see below for feature requests):
What’s going wrong?
npm install any-module-name-here
fails with «Maximum call stack size exceeded».
I’m using npm@5 in various projects already, but it fails to install / update in a project where I have local symlinks in the tree. At least, that is the only difference I can tell so far.
How can the CLI team reproduce the problem?
I can’t share the repo, but I managed to fix the issues with two small hacks:
First npm crashed with «Maximum call stack size exceeded» and this log:
0 info it worked if it ends with ok
1 verbose cli [ '/usr/local/Cellar/node@6/6.10.2/bin/node',
1 verbose cli '/usr/local/bin/npm',
1 verbose cli 'install',
1 verbose cli '@studio/backend' ]
2 info using npm@5.0.3
3 info using node@v6.10.2
4 verbose npm-session 3581873a35a3b427
5 silly install loadCurrentTree
6 silly install readLocalPackageData
7 http fetch GET 304 https://registry.npmjs.org/@studio%2fbackend 761ms (from cache)
8 silly pacote range manifest for @studio/backend@^1.6.0 fetched in 783ms
9 verbose stack RangeError: Maximum call stack size exceeded
9 verbose stack at /usr/local/lib/node_modules/npm/lib/install/node.js:33:42
9 verbose stack at Array.forEach (native)
9 verbose stack at exports.create (/usr/local/lib/node_modules/npm/lib/install/node.js:33:25)
9 verbose stack at /usr/local/lib/node_modules/npm/lib/install/node.js:36:14
9 verbose stack at Array.forEach (native)
9 verbose stack at exports.create (/usr/local/lib/node_modules/npm/lib/install/node.js:33:25)
9 verbose stack at normalizeTree (/usr/local/lib/node_modules/npm/lib/install.js:377:5)
9 verbose stack at Array.forEach (native)
9 verbose stack at normalizeTree (/usr/local/lib/node_modules/npm/lib/install.js:379:19)
9 verbose stack at Array.forEach (native)
9 verbose stack at normalizeTree (/usr/local/lib/node_modules/npm/lib/install.js:379:19)
9 verbose stack at Array.forEach (native)
9 verbose stack at normalizeTree (/usr/local/lib/node_modules/npm/lib/install.js:379:19)
9 verbose stack at Array.forEach (native)
9 verbose stack at normalizeTree (/usr/local/lib/node_modules/npm/lib/install.js:379:19)
9 verbose stack at Array.forEach (native)
10 verbose cwd /Users/max/projects/studio-analyzer
11 verbose Darwin 16.5.0
12 verbose argv "/usr/local/Cellar/node@6/6.10.2/bin/node" "/usr/local/bin/npm" "install" "@studio/backend"
13 verbose node v6.10.2
14 verbose npm v5.0.3
15 error Maximum call stack size exceeded
16 verbose exit [ 1, true ]
So I went into /usr/local/lib/node_modules/npm/lib/install.js
and changed normalizeTree
to this:
function normalizeTree (tree, seen = new Set()) { if (seen.has(tree)) { return; } seen.add(tree); createNode(tree) tree.location = flatNameFromTree(tree) tree.children.forEach((child) => normalizeTree(child, seen)) }
Secondly, npm install that-module
failed with «Cannot read property ‘type’ of undefined» and this log:
0 info it worked if it ends with ok
1 verbose cli [ '/usr/local/Cellar/node@6/6.10.2/bin/node',
1 verbose cli '/usr/local/bin/npm',
1 verbose cli 'i',
1 verbose cli '@studio/backend' ]
2 info using npm@5.0.3
3 info using node@v6.10.2
4 verbose npm-session 9f7ff18ce9a433f1
# heaps of logs with the ideal tree and detail about all my private modules
558 verbose stack TypeError: Cannot read property 'type' of undefined
558 verbose stack at /usr/local/lib/node_modules/npm/lib/shrinkwrap.js:111:12
558 verbose stack at Array.forEach (native)
558 verbose stack at shrinkwrapDeps (/usr/local/lib/node_modules/npm/lib/shrinkwrap.js:103:97)
558 verbose stack at /usr/local/lib/node_modules/npm/lib/shrinkwrap.js:138:7
558 verbose stack at Array.forEach (native)
558 verbose stack at shrinkwrapDeps (/usr/local/lib/node_modules/npm/lib/shrinkwrap.js:103:97)
558 verbose stack at treeToShrinkwrap (/usr/local/lib/node_modules/npm/lib/shrinkwrap.js:93:5)
558 verbose stack at /usr/local/lib/node_modules/npm/lib/shrinkwrap.js:76:21
558 verbose stack at done (/usr/local/lib/node_modules/npm/lib/utils/lifecycle.js:136:5)
558 verbose stack at LOOP (/usr/local/lib/node_modules/npm/node_modules/slide/lib/chain.js:7:26)
558 verbose stack at /usr/local/lib/node_modules/npm/node_modules/slide/lib/chain.js:18:7
558 verbose stack at /usr/local/lib/node_modules/npm/lib/utils/lifecycle.js:333:20
558 verbose stack at /usr/local/lib/node_modules/npm/node_modules/graceful-fs/polyfills.js:284:29
558 verbose stack at FSReqWrap.oncomplete (fs.js:123:15)
559 verbose cwd /Users/max/projects/studio-analyzer
560 verbose Darwin 16.5.0
561 verbose argv "/usr/local/Cellar/node@6/6.10.2/bin/node" "/usr/local/bin/npm" "i" "@studio/backend"
562 verbose node v6.10.2
563 verbose npm v5.0.3
564 error Cannot read property 'type' of undefined
565 verbose exit [ 1, true ]
This was also easy to fix in /usr/local/lib/node_modules/npm/lib/shrinkwrap.js
. Basically getRequested(child)
may return undefined, so I added this below line 110:
… and 🎉 npm install that-module
worked again.
Hope this helps fixing those issue.
supporting information:
npm -v
prints: 5.0.3node -v
prints: v6.10.2npm config get registry
prints: https://registry.npmjs.org/- Windows, OS X/macOS, or Linux?: macOS
- Network issues:
- Geographic location where npm was run:
- I use a proxy to connect to the npm registry.
- I use a proxy to connect to the web.
- I use a proxy when downloading Git repos.
- I access the npm registry via a VPN
- I don’t use a proxy, but have limited or unreliable internet access.
- Container:
- I develop using Vagrant on Windows.
- I develop using Vagrant on OS X or Linux.
- I develop / deploy using Docker.
- I deploy to a PaaS (Triton, Heroku).
Ситуация: заказчик попросил разместить на странице кликабельную картинку, а чтобы на неё обратило внимание больше посетителей, попросил сделать вокруг неё моргающую рамку. Логика моргания в скрипте очень простая:
- В первой функции находим на странице нужный элемент.
- Добавляем рамку с какой-то задержкой (чтобы она какое-то время была на экране).
- Вызываем функцию убирания рамки.
- Внутри второй функции находим тот же элемент на странице.
- Убираем рамку с задержкой.
- Вызываем первую функцию добавления рамки.
Код простой, поэтому делаем всё в одном файле:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Pulse</title>
<style type="text/css">
/* рамка, которая будет моргать */
.pulse { box-shadow: 0px 0px 4px 4px #AEA79F; }
</style>
</head>
<body>
<div id="pulseDiv">
<a href="#">
<div id="advisersDiv">
<img src="https://thecode.media/wp-content/uploads/2020/08/photo_2020-08-05-12.04.57.jpeg">
</div>
</a>
</div>
<!-- подключаем jQuery -->
<script src="https://yastatic.net/jquery/3.3.1/jquery.min.js" type="text/javascript"></script>
<!-- наш скрипт -->
<script type="text/javascript">
// добавляем рамку
function fadeIn() {
// находим нужный элемент и добавляем рамку с задержкой
$('#pulseDiv').find('div#advisersDiv').delay(400).addClass("pulse");
// затем убираем рамку
fadeOut();
};
// убираем рамку
function fadeOut() {
// находим нужный элемент и убираем рамку с задержкой
$('#pulseDiv').find('div#advisersDiv').delay(400).removeClass("pulse");
// затем добавляем
fadeIn();
};
// запускаем моргание рамки
fadeIn();
</script>
</body>
</html>
Но при открытии страницы в браузере мы видим, что ничего не моргает, а в консоли появилась ошибка:
❌ Uncaught RangeError: Maximum call stack size exceeded
Что это значит: в браузере произошло переполнение стека вызовов и из-за этого он не может больше выполнять этот скрипт.
Переполнения стека простыми словами означает вот что:
- Когда компьютер что-то делает, он это делает последовательно —
1
,2
,3
,4
. - Иногда ему нужно отвлечься от одного и сходить сделать что-то другое — а, б, в, г, д. Получается что-то вроде
1
,2
,3 → а
,б
,в
,г
,д → 4
. - Вот эти переходы
3 → а
ид → 4
— это компьютеру нужно запомнить, что он выполнял пункт 3, и потом к нему вернуться. - Каждое запоминание, что компьютер бросил и куда ему нужно вернуться, — это называется «вызов».
- Вызовы хранятся в стеке вызовов. Это стопка таких ссылок типа «когда закончишь вот это, вернись туда».
- Стек не резиновый и может переполняться.
Что делать с ошибкой Uncaught RangeError: Maximum call stack size exceeded
Эта ошибка — классическая ошибка переполнения стека во время выполнения рекурсивных функций.
Рекурсия — это когда мы вызываем функцию внутри самой себя, но чуть с другими параметрами. Когда параметр дойдёт до конечного значения, цепочка разматывается обратно и функция собирает вместе все значения. Это удобно, когда у нас есть чёткий алгоритм подсчёта с понятными правилами вычислений.
В нашем случае рекурсия возникает, когда в конце обеих функций мы вызываем другую:
- Функции начинают бесконтрольно вызывать себя бесконечное число раз.
- Стек вызовов начинает запоминать вызов каждой функции, чтобы, когда она закончится, вернуться к тому, что было раньше.
- Стек — это определённая область памяти, у которой есть свой объём.
- Вызовы не заканчиваются, и стек переполняется — в него больше нельзя записать вызов новой функции, чтобы потом вернуться обратно.
- Браузер видит всё это безобразие и останавливает скрипт.
То же самое будет, если мы попробуем запустить простую рекурсию слишком много раз:
Как исправить ошибку Uncaught RangeError: Maximum call stack size exceeded
Самый простой способ исправить эту ошибку — контролировать количество рекурсивных вызовов, например проверять это значение на входе. Если это невозможно, то стоит подумать, как можно переделать алгоритм, чтобы обойтись без рекурсии.
В нашем случае проблема возникает из-за того, что мы вызывали вторые функции бесконтрольно, поэтому они множились без ограничений. Решение — ограничить вызов функции одной секундой — так они будут убираться из стека и переполнения не произойдёт:
<script type="text/javascript">
// добавляем рамку
function fadeIn() {
// находим нужный элемент и добавляем рамку с задержкой
$('#pulseDiv').find('div#advisersDiv').delay(400).addClass("pulse");
// через секунду убираем рамку
setTimeout(fadeOut,1000)
};
// убираем рамку
function fadeOut() {
// находим нужный элемент и убираем рамку с задержкой
$('#pulseDiv').find('div#advisersDiv').delay(400).removeClass("pulse");
// через секунду добавляем рамку
setTimeout(fadeIn,1000)
};
// запускаем моргание рамки
fadeIn();
</script>
Вёрстка:
Кирилл Климентьев
Привет, Хабровчане!
Большинство разработчиков, которые использовали рекурсию для решения своих задач, видели такую ошибку:
RangeError: Maximum call stack size exceeded.
Но не каждый разработчик задумывался о том, а что означает «размер стэка вызовов» и каков же этот размер? А в чем его измерять?
Думаю, те, кто работают с языками, напрямую работающими с памятью, смогут легко ответить на этот вопрос, а вот типичный фронтэнд разработчик скорее всего задает себе подобные вопросы впервые. Что-ж, попробуем разобраться!
Многие полагают, что браузер ограничивает нас именно в количестве вызовов, но это не так. В данной статье я покажу на простых примерах, как это работает на самом деле.
О чем ты вообще, автор?
Для статьи важно понимание таких понятий как Execution Stack, Execution Context. Если вы не знаете, что это такое, то советую об этом почитать. На данном ресурсе уже было достаточно хороших статей на эту тему. Пример — https://habr.com/ru/company/ruvds/blog/422089/
Когда возникает эта ошибка?
Разберем на простом примере — функция, которая рекурсивно вызывает сама себя.
const func = () => {
func();
}
Если попытаться вызвать такую функцию, то мы увидим в консоли/терминале ошибку, о которой я упомянул выше.
А что если подглядеть, сколько же раз выполнилась функция перед тем, как возникла ошибка?
На данном этапе код запускается в Chrome DevTools последней версии на март 2021. Результат будет различаться в разных браузерах. В дальнейшем в статье я упомяну об этом.
Для эксперимента будем использовать вот такой код:
let i = 0;
const func = () => {
i++;
func();
};
try {
func();
} catch (e) {
// Словили ошибку переполнения стэка и вывели значение счетчика в консоль
console.log(i);
}
Результатом вывода в консоль стало число в 13914. Делаем вывод, что перед тем, как переполнить стэк, наша функция вызвалась почти 14 тысяч раз.
Магия начинается тогда, когда мы начинаем играться с этим кодом. Допустим, изменим его вот таким образом:
let i = 0;
const func = () => {
let someVariable = i + 1;
i++;
func();
};
try {
func();
} catch (e) {
console.log(i);
}
Единственное, что мы добавили, это объявление переменной someVariable
в теле функции. Казалось бы, ничего не поменялось, но число стало меньше. На этот раз функция выполнилась 12523 раз. Что более чем на тысячу меньше, чем в прошлом примере. Чтобы убедиться, что это не погрешность, пробуем выполнять такой код несколько раз, но видим одни и те же результаты в консоли.
Почему же так? Что изменилось? Как понять, посмотрев на функцию, сколько раз она может выполниться рекурсивно?!
Магия раскрыта
Отличие второго примера от первого — наличие дополнительной переменной внутри тела функции. На этом все. Соответственно, можно догадаться, именно из-за этого максимальное количество рекурсивных вызовов стало меньше. Что-ж, а что, если у нас будет не одна, а четыре переменных внутри функции? По этой логике, количество рекурсивных вызовов станет еще меньше? Проверим:
let i = 0;
const func = () => {
let a = i + 1;
let b = a + 1;
let c = b + 1;
let d = c + 1;
let e = d + 1;
i++;
func();
};
try {
func();
} catch (e) {
console.log(i);
}
По этой логике, значение счетчика должно стать еще меньше. Выдыхая, видим вывод — 8945. Ага, оно стало еще меньше. Значит, мы на правильном пути. Хочу привести небольшую аналогию, чтобы даже самым маленьким стало понятно.
Execution Stack (Call Stack) — это емкость с водой. Изначально она пустая. Так получилось, что эта емкость с водой стоит прямо на электрической проводке. Как только емкость переполнится, вода проливается на провода, и мы видим ошибку в консоли. При каждом новом рекурсивном вызове функции в стэк падает капелька воды. Само собой, чем капелька воды крупнее, тем быстрее наполнится стэк.
Емкости бывают разные по размеру. И размер емкости в данном примере — это размер коллстэка. А точнее — количество байт, которое он максимально может в себе удержать. Как гласит статья про Call Stack (Execution Stack), которую я приложил в начале, на каждый вызов функции создается Execution Context — контекст вызова функции (не путать с this). И упрощая, в нем, помимо разных «подкапотных» штук, содержатся все переменные, которые мы объявили внутри функции. Как Execution Context, так и каждая переменная внутри него имеют определенный размер, который они занимают в памяти. Сложив эти два размера мы и получим «размер» капли, которая капает в кувшин при каждом рекурсивном вызове функции.
У нас уже достаточно много данных. Может быть, поэкспериментируем еще? А давайте попробуем вычислить, какой размер коллстэка в движке, который использует Chrome?
Математика все-таки пригодилась
Как мы выяснили, у нас есть две неизвестные, которые составляют размер функции (капельки, которая падает в емкость). Это размер самого Execution Stack, а так же сумма размеров всех переменных внутри функции. Назовем первую N, а вторую K. Сам же неизвестный размер коллстэка обозначим как X.
В итоге — количество байт, которое занимает функция (в упрощенном виде) будет:
SizeOfVar в данном случае — количество байт, которые занимает переменная в памяти.
Учитывая, что мы знаем количество вызовов первой функции, в теле которой не объявляются переменные, размер коллстэка можно выразить как:
И, для второго случая, возьмем функцию, внутри которой было объявлено пять переменных.
Иксы в обоих случаях обозначают одно и то же число — размер коллстэка. Как учили в школе, можем приравнять правые части уравнений.
Выглядит неплохо. У нас тут две неизвестные переменные — N и SizeOfVar. Если N мы не можем откуда-то узнать, то что насчет SizeOfVar? Заметим, что во всех функциях, которые фигурировали выше, переменные хранили значение с типом «number», а значит, нужно просто узнать, сколько же байт в памяти занимает одна такая переменная.
С помощью великого гугла получаем ответ — «Числа в JavaScript представлены 64-битными значениями с плавающей запятой. В байте 8 бит, в результате каждое число занимает 64/8 = 8 байт.» Вот она — последняя неизвестная. 8 байт. Подставляем ее в наше уравнение и считаем, чем равно N.
Упрощаем:
Если выразить отсюда N, то получим ответ: N равно приблизительно 72. В данном случае 72 байтам.
Теперь, подставив N = 72 в самое первое уравнение, получим, что размер коллстэка в Chrome равен… 1002128 байтов. Это почти один мегабайт. Не так уж и много, согласитесь.
Мы получили какое-то число, но как убедиться, что наши расчеты верны и число правильное? А давайте попробуем с помощью этого числа спрогнозировать, сколько раз сможет выполниться функция, внутри которой будет объявлено 7 переменных типа ‘number’.
Считаем: Ага, каждая функция будет занимать (72 + 7 * байт, это 128. Разделим 1002128 на 128 и получим число… 7829! Согласно нашим расчетам, такая функция сможет рекурсивно вызваться именно 7829 раз! Идем проверять это в реальном бою…
Мы были очень даже близки. Реальное число отличается от теоретического всего на 3. Я считаю, что это успех. В наших расчетах было несколько округлений, поэтому результат мы посчитали не идеально, но, очень-очень близко. Небольшая погрешность в таком деле — нормально.
Получается, что мы посчитали все верно и можем утверждать, что размер пустого ExecutionStack в Chrome равен 72 байтам, а размер коллстэка — чуть меньше одного мегабайта.
Отличная работа!
Важное примечание
Размер стэка разнится от браузера к браузеру. Возьмем простейшую функцию из начала статьи. Выполнив ее в Сафари получим совершенно другую цифру. Целых 45606 вызовов. Функция с пятью переменными внутри выполнилась бы 39905 раз. В NodeJS числа очень близки к Chrome по понятным причинам. Любопытный читатель может проверить это самостоятельно на своем любимом движке JavaScript.
А что с непримитивами?
Если с числами все вроде бы понятно, то что насчет типа данных Object?
let i = 0;
const func = () => {
const a = {
key: i + 1,
};
i++;
func();
};
try {
func();
} catch (e) {
console.log(i);
}
Простейший пример на ваших глазах. Такая функция сможет рекурсивно вызваться 12516. Это практически столько же, сколько и функция с одной переменной внутри. Тут в дело вступает механизм хранения и передачи объектов в JS’е — по ссылке. Думаю, большинство уже знают об этом.
А что с этим? А как поведет себя вот это? А что с *?
Как вы заметили, экспериментировать можно бесконечно. Можно придумать огромное количество кейсов, которые будут прояснять эту ситуацию глубже и глубже. Можно ставить эксперименты при разных условиях, в разных браузерах, на разных движках. Оставляю это на тех, кого эта тема заинтересовала.
Итоги:
-
Количество рекурсивных вызовов функции до переполнения стэка зависит от самих функций.
-
Размер стэка измеряется в байтах.
-
Чем «тяжелее» функция, тем меньше раз она может быть вызвана рекурсивно.
-
Размер стэка в разных движках различается.
Вопрос особо любознательным: А сколько переменных типа «number» должно быть объявлено в функции, чтобы она могла выполниться рекурсивно всего два раза, после чего стэк переполнится?
The JavaScript RangeError: Maximum call stack size exceeded
is an error that occurs when there are too many function calls, or if a function is missing a base case.
Error message:
RangeError: Maximum call stack size exceeded
Error Type:
RangeError
What Causes RangeError: Maximum Call Stack Size Exceeded
The RangeError: Maximum call stack size exceeded
is thrown when a function call is made that exceeds the call stack size. This can occur due to the following reasons:
- Too many function calls.
- Issues in handling recursion, e.g. missing base case in a recursive function to stop calling itself infinitely.
- Out of range operations.
RangeError: Maximum Call Stack Size Exceeded Example
Here’s an example of a JavaScript RangeError: Maximum call stack size exceeded
thrown when using a recursive function that does not have a base case:
function myFunc() {
myFunc();
}
myFunc();
Since the recursive function myFunc()
does not have a terminating condition (base case), calling it creates an infinite loop as the function keeps calling itself over and over again until the RangeError: Maximum call stack size exceeded
error occurs:
Uncaught RangeError: Maximum call stack size exceeded
at myFunc (test.js:2:2)
at myFunc (test.js:2:2)
at myFunc (test.js:2:2)
at myFunc (test.js:2:2)
at myFunc (test.js:2:2)
at myFunc (test.js:2:2)
at myFunc (test.js:2:2)
at myFunc (test.js:2:2)
at myFunc (test.js:2:2)
at myFunc (test.js:2:2)
How to Avoid RangeError: Maximum Call Stack Size Exceeded
If this error is encountered when calling recursive functions, it should be ensured that the function has a defined base case to terminate the recursive calls.
In case this error occurs due to an excessive number of function calls or variables, these should be reduced as much as possible. Any out of range operations should also be checked and avoided. These issues can be inspected using the browser console and developer tools.
The earlier example can be updated to include a base case:
function myFunc(i) {
if (i >= 5) {
return;
}
myFunc(i+1);
}
myFunc(1);
The above code avoids the error since recursive calls terminate when the base case is met.
Track, Analyze and Manage Errors With Rollbar
Managing errors and exceptions in your code is challenging. It can make deploying production code an unnerving experience. Being able to track, analyze, and manage errors in real-time can help you to proceed with more confidence. Rollbar automates error monitoring and triaging, making fixing JavaScript errors easier than ever. Sign Up Today!