Course: JavaScript

Progress (0%)

JavaScript while Loop

Chapter 26 19 mins

Learning outcomes:

  1. What is the while loop meant for
  2. Syntax of while
  3. Basic while examples
  4. The do...while loop
  5. Nested loops

Introduction

As we learnt in the previous chapter on the for loop, there are essentially two main categories of loops in JavaScript — for and while. We've already covered for quite extensively, and likewise what's left now is while.

In this chapter, we aim to unravel the purpose behind while in addition to considering an array of various examples of its usage. Without any single doubt, while forms a crucial part of modern-day programming. Many many algorithms are powered by the powerful nature of while.

So what is while? Let's see it...

What is while meant for?

By design:

The while loop is meant to repeatedly execute a piece of code an unknown number of times.

For instance, if we want to print 'Hello' until the user enters -1 in an input prompt, what we need is the while loop.

Similarly, if we want to iterate over a queue, removing items one-by-one, until there is no item left, this could also be done using a while loop.

Technically speaking, anything that can be done, or is done, using while can also be done using for, and vice versa. It's just that the syntax of while sometimes suits the situation better and obviously asks for less setup than a for loop.

Let's now see the syntax of while.

Syntax of while

The while loop begins with the while keyword, followed by a pair of parentheses (()) encapsulating the iteration condition, followed by the loop's body.

Here's a view of this syntax:

while (condition) statement;

The loop continues iterating as long as the given condition remains truthy. The moment it becomes false, the loop ends.

If you recall, this is identical to how the condition in a for loop works — that is, if it's true, the next iteration happens, or else the loop ends.

With the syntax also done, it's finally time to consider some quick examples.

Examples of while

Summing some positive numbers

Suppose we want to compute the sum a couple of positive numbers entered by the user.

The way the entry happens is via an input prompt for the first number, followed by another input prompt for the second number, and so on, until the value entered is -1. -1 signals the end of the whole stream of numbers to add.

Such a special value is often referred to as a sentinel. A loop that iterates up until the point a sentinel is seen is, likewise, referred to as a sentinel-controlled loop.

In our case, -1 works as a sentinel because we want to add positive numbers only; -1 being a negative number is a good candidate for signalling the end of our inputs.

Anyways, this task can very easily be accomplished with the help of while as shown below:

var sum = 0;
var input = prompt('Enter a number, -1 to exit.');

while (input !== '-1') {
   sum += Number(input);
   input = prompt('Enter a number, -1 to exit.');
}

alert(`The sum is ${sum}.`);

Live Example

If we were to accomplish the same thing using for, we'd go like:

var input = prompt('Enter a number, -1 to exit.');

for (var sum = 0; input !== '-1'; ) {
   sum += Number(input);
   input = prompt('Enter a number, -1 to exit.');
}

alert(`The sum is ${sum}.`);

See how the syntax of for is a little bit less intuitive and a little more to type. For example, the third part of the loop's header, i.e. the update expression, is omitted, and rightly so — we don't need it.

Clearly, while is the better choice in this case.

Recall from the previous chapter that for is better suited to iterate a known number of times, for e.g. iterating over a string, over an array, an n number of times, and so on.

Removing array elements

As another example, suppose we want to remove elements from the end of an array, using the pop() method, until it becomes empty. This also can be done nicely using a while loop.

Consider the following code:

var arr = [1, 3, 5];

while (arr.length !== 0) {
   console.log(arr.pop());
}

The condition arr.length !== 0 checks if the array has anything remaining in it. If there is something remaining, the condition evaluates to true, and hence the loop's body executes, thereby removing the last element from the array (via the arr.pop() call).

5 3 1

Note that arr.pop() effectively reduces the length of arr as the loop progresses and thus takes it closer and closer to the success of the condition arr.length !== 0. Without arr.pop(), we'd get an infinite loop.

Recall that an infinite loop is one that runs forever, since it's condition is always true, and ultimately causes the program to simply crash.

Although the code above works perfectly, you'll often see one common convention used out there to check whether an array has elements remaining within it.

That is, for a given array arr, instead of using arr.length !== 0 to check whether there are any remaining elements in arr, the expression arr.length is used.

When it coerces to true, it simply means that the array's length is not 0 and hence there is still something remaining in it. This is effectively identical to arr.length !== 0.

Likewise, the code above can be rewritten as follows:

var arr = [1, 3, 5];

while (arr.length) {
   console.log(arr.pop());
}
5 3 1

The while loop here can be read as follows: "While the array has some length, remove its last element."

The do...while loop

One often-overlooked variation of the while loop, with a sligtly different syntax, is the do...while loop. It serves a crucial purpose that the typical while doesn't.

do...while first evaluates the loop's body and then checks the condition (for the next iteration).

Hence, when the given condition is false to begin with, do...while still executes the body once unlike a normal while loop.

First, let's see the syntax of do...while:

do statement;
while (condition);

As before, statement is the loop's body whereas condition is the iteration condition.

It's invalid to put two semicolons (;) at the end of the do statement. That's because after the first semicolon (which is optional), the do statement ends, and likewise a while is expected before the second semicolon. Putting two semicolons one after another simply misses out the while statement, and this is the reason why doing so is syntactically invalid.

Now you might ask what's the benefit of this approach?

Well, sometimes we do want to execute a certain piece of code and only after that check for some condition, before executing the code again.

A common example would be obtaining an input again and again till the entered value is valid.

Consider the following code:

do {
   var input = prompt('Enter a number');
}
while (isNaN(Number(input)));

alert(`You entered the number ${input}`);

Live Example

First we have the do block with one statement, asking for the user's input which must be a valid number. Next, in the while part of the loop, we check whether the value input coerces to NaN. If it does, this means that the provided input was some arbitrary value not able to be coerced into a number, and so likewise we must repeat the prompt.

In words, we can read the code above as "keep asking for the user's input while the entered value is NaN."

Such a loop that runs until an entered value is valid is often referred to as an input-validation loop.

Note that the code above can be rewritten using a plain while loop as well. Below shown is an illustration:

var input = prompt('Enter a number');

while (isNaN(Number(input))) {
   input = prompt('Enter a number');
}

alert(`You entered the number ${input}`);

Notice that we have to repeat the loop's body before the actual while statement. This is so that input can be set to a particular value before executing the loop, asking the user to input again.

One obvious way out of this repetition is to predefine the variable input with a dummy value (undefined will also work in this case) that'll get at least the first iteration of the loop to execute.

For instance, we can set input to NaN ourself, and thus get the loop's condition to be met, thereby leading to the execution of the input prompt, after which input will be evaluated once again for the next iteration.

// Set to NaN in order to execute the following loop at least once.
var input = NaN;

while (isNaN(Number(input))) {
   input = prompt('Enter a number');
}

alert(`You entered the number ${input}`);

This technique surely works, but it's not really that intuitive.

Ideally, we should use do...while for such a scenario since it was made purposefully for such a scenario. Moreover, with this approach, we have to predeclare the variables involved in the loop's condition before the while statement.

This doesn't have to be done with a do...while loop — the respective variables can be defined right inside the do block and then remain accessible in the while clause.

To boil it down, whenever we wish to execute a piece of code repeatedly but check the condition after the code (not before it), we must use a do...while loop. It might be an occasional programming construct but when used, is extremely versatile and elegant.

Nested loops

What's more interesting in a program than a set of nested loops. And if those loops all belong to the while family, things become yet more interesting.

So what do you say? Shall we consider a complicated example of nested loops?

Let's suppose we want to ask the user to input a list of numbers.

Each number is entered in a separate input prompt. If the entered value is not a number, an error message is output followed by asking the user to input again. This goes on until the sentinel value -1 is entered, marking the the end of the whole list.

Here's the code that does this using two do...while loops:

var sum = 0;

do {
   // Obtain the correct input
   do {
      var inputNumber = Number(prompt('Enter a number, -1 to end.'));
      if (isNaN(inputNumber)) {
         alert('Entered value is invalid.');
      }
   }
   while (isNaN(inputNumber));

   // Add the input
   if (inputNumber !== -1) {
      sum += inputNumber;
   }
}
while (inputNumber !== -1);

alert(`The sum is ${sum}.`);

Live Example

The outer loop is a sentinel-controlled loop that iterates until the point the value -1 is input. With each iteration of this outer loop, the entered value is added to the previous sum stored in sum, but only if the entered number is not -1.

Contrary to this, the inner loop is an input-validation loop that iterates until the point the value entered is valid. Once the input is valid, the inner loop ends and likewise provides the correct value of inputNumber to the following code, i.e. line 12 where the addition takes place.

Note that the task above can also be accomplished using continue in lieu of a set of nested loops. Here's one way to rewrite the code above:

var sum = 0;

do {
   var inputNumber = Number(prompt('Enter a number, -1 to end.'));

   if (isNaN(inputNumber)) {
      alert('Entered value is invalid.');
      continue;
   }

   if (inputNumber !== -1) {
      sum += inputNumber;
   }
}
while (inputNumber !== -1);

alert(`The sum is ${sum}.`);

The inner loop is gone and in place of that we have a simple condition to check if the input number is NaN. If it really is NaN, the error message is shown (as before) followed by the continue statement.

Recall that continue effectively skips any code that comes after it and continues execution with the next iteration.

In the code above, when continue is encountered, the entire logic in lines 11 - 13 is ignored and execution tips back to the beginning the loop (obviously after checking the condition), thus making the desired input prompt again.

Although this code does the same job as the previous one, you may agree that it's slightly less readable.

As we stated before, continue and break tend to introduce holes into the reading flow of a program — we can't really be sure whether any following code will be executed until and unless we go through the entire loop containing these control-flow commands. Without them, the code speaks of itself.

Let's review the same code above that contained a set of nested loops and see how exactly is it more readable than the one with continue:

var sum = 0;

do {
   // Obtain the correct input.
   do {
      var inputNumber = Number(prompt('Enter a number, -1 to end.'));
      if (isNaN(inputNumber)) {
         alert('Entered value is invalid.');
      }
   }
   while (isNaN(inputNumber));

   // Add the input, if not -1.
   if (inputNumber !== -1) {
      sum += inputNumber;
   }
}
while (inputNumber !== -1);

alert(`The sum is ${sum}.`);

The outer loop is the same as before so there is really nothing special to consider in it.

The inner loop is the main point. It is tasked with obtaining the correct input and then giving us back the respective inputNumber. That is, by line 12, we are sure that inputNumber is valid. Moreover, with this inner loop instead of continue, there aren't any holes while reading the given program.

All in all, whenever possible, resort to using basic programming constructs to achieve a given task, rather than using the control-flow commands continue and break.

"I created Codeguage to save you from falling into the same learning conundrums that I fell into."

— Bilal Adnan, Founder of Codeguage