Course: JavaScript

Progress (0%)

  1. Foundation

  2. Numbers

  3. Strings

  4. Conditions

  5. Loops

  6. Arrays

  7. Functions

  8. Objects

  9. Exceptions

  10. HTML DOM

  11. CSSOM

  12. Events

  13. Drag and Drop

  14. opt Touch Events

  15. Misc

  16. Project: Analog Clock

JavaScript while Loop

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 condition of iteration, followed by the loop's body.

Here's a programmatic view of this syntax:

while (condition) statement;

The loop continues iterating as long as the given condition remains true. 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

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.

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 sum = 0;
var input = prompt('Enter a number, -1 to exit.');

for (; 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. In this case, while is definitely the better choice.

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());
}
5 3 1

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).

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 and causes the current 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: "while the array has some length, keep removing its last element."

Once again, remember that if arr.length returns a positive number (which means that arr has some elements in it), it would coerce to the Boolean value true, and thus get the loop to be executed.

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.

That is,

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.

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 think? Shall we consider a complicated nested loops example?

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:

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, is not -1.
   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 up 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 (line 12).

Contrary to this, the inner loop is an input-validation loop that iterates up 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 without using 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 value 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 on execution with the next iteration. In the code above, when continue is encountered, all the logic from lines 11 - 13 is ignored and the loop runs again from line 4, thus making the desired input prompt again.

Although this code does the same job as the previous one, it is 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 keywords.

However, 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, we must resort to using basic programming constructs to achieve a given task, rather than using the control-flow commands continue and break.