Movatterモバイル変換


[0]ホーム

URL:


Skip to content
DEV Community
Log in Create account

DEV Community

Jan Küster 🔥
Jan Küster 🔥

Posted on

     

Prevent infinite loops in JavaScript

There are some typical beginner (YES, AND EXPERT-) errors in JavaScript that can be a real pain:infinite loops ♾️

They occur, when the control structures have no condition to end (branch out) and thus execute indefinitely.

This guide covers some causes of infinite loops and how to prevent them.

1. The no-brainer
2. The forgotten increment / decrement
3. The uncontrollable source of truth
4. The unintended override
5. The unorthodox breaking condition
6. More options to prevent infinite loops



1. ♾️ The no-brainer

If you never have encountered an infinite loop, simply run the following code. Do not use your developer console to directly execute code but use online tools like CodePen, otherwise you may have to force-quit the whole browser process (depending on your os, browser and version):

while(true){console.log('pleas wait just one more second')}
Enter fullscreen modeExit fullscreen mode

or even shorter:

while(true);
Enter fullscreen modeExit fullscreen mode

That's a nasty experience and when one of your users gets into this situation, you can be sure you have just lost her.

How to prevent

Run brain.exe before coding. I think the vast majority will not face this one unless intentionally done.

If this really happens to you by accident: it's time to go home for today.



2. ♾️ The forgotten increment / decrement

This is a classic one and even experienced developers fall into it from time to time, especially when working too long and concentration(tm) has left the chat:

leti=0while(i<10){console.log(i)// i++ is missing}
Enter fullscreen modeExit fullscreen mode

Similar with decrementing:

leti=9while(i>=0){console.log(i)// i-- is missing}
Enter fullscreen modeExit fullscreen mode

Prevent it using auto-increment / auto-decrement

For beginners this might be confusing, due to way how pre-increment and post-increment (and *-decrement) work.

Suggestion:read it up first and then use it directly in your while conditional:

leti=0while(i++<10){console.log(i)// will be 1,2,3,4,5,6,7,8,9,10}
Enter fullscreen modeExit fullscreen mode

As you can see, this will not counti from0 ... 9 so we need to fix the indices:

leti=-1while(i++<9){console.log(i)// will be 0,1,2,3,4,5,6,7,8,9}
Enter fullscreen modeExit fullscreen mode

Yes, I know it becomes rather confusing than it helps. This is because thei is incrementedbefore the body is executed (as opposed tofor loops, where it will be incrementedafter the body has been executed). Just keep it in mind when you next time design a while loop with auto-increment.

With pre-increment (already range-corrected):

leti=-1while(++i<=9){console.log(i)// will be 0,1,2,3,4,5,6,7,8,9}
Enter fullscreen modeExit fullscreen mode

A good exercise in between: Implement the same while loop with auto pre-decrement (--i) and auto post-decrement (i--).



3. ♾️ The uncontrollable source of truth

Sometimes you usewhile loops to do some operation until a condition is met and where the breaking condition is not based on numerical values.

If the source of this condition is hardly determinable (as opposed to counters with a numerical limit) you may face infinite loops. The worst: in rare cases these may occur only in a few situations to a few users and debugging sessions will be long and exhaustive!

letended=falsewhile(!ended){// do stuffended=getIsEnded()// ticking time-bomb}
Enter fullscreen modeExit fullscreen mode

Use a safety counter to prevent this

If you really can't redesign this one towards a more determined condition then you may introduce some kind of safetey-counter.

This counter will be the super-most-upper-maximum of iterations that run and if it's reached you expect the loop to run into infinity mode and throw an Error to prevent this:

letended=falseletsafety=0constmaxSafety=1000while(!ended&&safety++<maxSafety){// do stuffended=getIsEnded()// just tick...}if(!ended){thrownewError('Infinite loop detected and prevented')}
Enter fullscreen modeExit fullscreen mode



4. ♾️ The unintended override

Let's say your code gets more and more complex and you will face situations, where your counter or condition is overridden or altered then you may not realize that this may lead into infinite loops:

constarray=[0,1,2,3]for(leti=0;i<array.length;i++){// do stuff...array.push(-1)// boom}
Enter fullscreen modeExit fullscreen mode

Another example:

constobj={count:i,max:10}constincrement=obj=>{obj.count++obj.max++// unintended but still boom}while(obj.count<obj.max){// do stuffincrement(obj)}
Enter fullscreen modeExit fullscreen mode

While this example is somewhat exotic and I suggest to never do such constructs it shows that some of JavaScript's features (pass object by reference) used the wrong way can easily cause trouble.

Prevent using immutable maximum

Using aconst for maximum values makes it much harder to manipulate the upper bounds:

constarray=[0,1,2,3]constlength=array.lengthfor(leti=0;i<length;i++){// do stuff...array.push(-1)// who cares}
Enter fullscreen modeExit fullscreen mode

Some goes for the while loop:

constmax=10constobj={count:0}constincrement=obj=>{obj.count++}while(obj.count<max){// do stuffincrement(obj)}
Enter fullscreen modeExit fullscreen mode

However, just don't use this second example at all and better rewrite your code to use independent variables:

constmax=10leti=0while(i<max){// do stuffi++}
Enter fullscreen modeExit fullscreen mode



5. ♾️ The unorthodox breaking condition

You can create some crazy complex conditions for breaking loops. This could also potentially cause infinite loops.

Consider a loop that breaks only if the counter is exactly a specific value (as opposed to using less-than or greater-than):

for(leti=0;i!==5;i++){console.log(i)// 0,1,2,3,4}
Enter fullscreen modeExit fullscreen mode

Yes it works and breaks as expected. But what if your counter is not incremented using the++ operator but, say using+= 3?

for(leti=0;i!==5;i+=3){console.log(i)// 0,3,6,9,12,15...}
Enter fullscreen modeExit fullscreen mode

Prevention options

First you can introduce a safety counter (as shown before) or add a more determinable condition:

for(leti=0;i!==5&&i<10;i+=3){console.log(i)// 0,3,6,9,12,15...}
Enter fullscreen modeExit fullscreen mode

Try to avoid breaking conditions that introduce the possibility to never occur.



6. 🔧 More options to prevent infinite loops

Iterate over iterable

Iterables are great as they can be safely iterated viafor..of and never cause infinite loopswhen only reading:

for(constnumof[0,1,2,3,4])console.log(num)// 0,1,2,3,4for(constcharof'hello')console.log(char)// h,e,l,l,ofor(constnameofnewSet(['jane','john']))console.log(name)// jane, john
Enter fullscreen modeExit fullscreen mode

Note, however, that extending / altering the structures during the loop will still be a potential cause for infinity loops!

Use a safe for-loop

The easiest way to prevent the loop is to use conditions that are always determined. The following for-loops are very good examples of that:

Iterate n-times in forward direction

for(leti=0;i<10;i++){...}
Enter fullscreen modeExit fullscreen mode

Iterate n-times in backwards direction

for(leti=9;i>=0;i--){...}
Enter fullscreen modeExit fullscreen mode

In both cases the loops will always run through (unless you try to manipulatei inside the body but I assume you know that would be a very dangerous thing to do).

Use a function with "safe-iteration"

You can write a function, that implements a loop in a safe way and which executes a given function in each step:

constloop=({n,fct,backwards})=>{letiif(backwards){for(i=n-1;i>=0;i--)fct(i)}// default mode is forwardelse{for(i=0;i<n;i++)fct(i)}}// usageloop({n:5,fct:i=>console.log(i)})// 0,1,2,3,4loop({n:5,fct:i=>console.log(i),backwards:true})// 4,3,2,1,0
Enter fullscreen modeExit fullscreen mode

Use a safe while-loop

The following function is an example of a while loop, wrapped in a more safe environment that will prevent infinite loops:

constsafeWhile=({condition,fct,max=1000})=>{leti=0letvalue// cover optional return valuewhile(condition(i)){if(i++>=max){thrownewError('Infinite loop detected and prevented')}value=fct(i)}returnvalue}// usagesafeWhile({condition:i=>true,fct:i=>{}})// throws error but is never infinite
Enter fullscreen modeExit fullscreen mode

Summary

I hope this collection of causes and fixes will help you to write more robust code and prevent these nasty infinite loops at all cost to maximize functionality and stability of your applications.

If you think there are concepts missing, confusing or simply wrong, please leave a comment so the article can be improved for everyone ❤️

Top comments(0)

Subscribe
pic
Create template

Templates let you quickly answer FAQs or store snippets for re-use.

Dismiss

Are you sure you want to hide this comment? It will become hidden in your post, but will still be visible via the comment'spermalink.

For further actions, you may consider blocking this person and/orreporting abuse

Graduated in Digital Media M.Sc. now developing the next generation of educational software. Since a while I develop full stack in Javascript using Meteor. Love fitness and Muay Thai after work.
  • Location
    Bremen, Germany
  • Education
    M.Sc.
  • Pronouns
    he/him
  • Work
    Scientific Employee at University of Bremen
  • Joined

More fromJan Küster 🔥

DEV Community

We're a place where coders share, stay up-to-date and grow their careers.

Log in Create account

[8]ページ先頭

©2009-2025 Movatter.jp