theEpsilon theEpsilon - 2 months ago 10
HTML Question

Functions giving unexpected behavior in JavaScript

I am writing a program to generate primes in JavaScript. The problem is that the behavior of the program is very strange, it seems that the functions do not execute in order which prevents it from executing properly. Here is my code:



<!DOCTYPE html>
<html>

<head>
<title>Generate Some Primes!</title>
</head>

<body>
<h1>Let's Generate Some Primes!!!</h1>
<form id="userInput">
<ul>
<li>
<label>Lower Bound (inclusive):</label>
<input type="number" id="lower">
</li>
<li>
<label>Upper Bound (exclusive):</label>
<input type="number" id="upper">
</li>
<li>
<input type="submit" value="Generate!">
</li>
</ul>
</form>
<p id="messageBox"></p>
<ul id="primes"></ul>
<script>
var primesList = document.getElementById("primes");
var lower;
var upper;
var NOT_STARTED = "Generating ...";
var DONE = "Done!";
window.onload = function() {
userInput.onsubmit = function(e) {
e.preventDefault();
writeMessage(NOT_STARTED);
if (checkInput()) {
checkNumbers();
writeMessage(DONE);
}
}
}

function checkInput() {
initializeBounds();
if (lower >= upper) {
writeMessage("Lower bound must be less than upper bound.");
return false;
}
return true;
}

function initializeBounds() {
lower = Math.round(document.getElementById("lower").value);
upper = Math.round(document.getElementById("upper").value);
primesList.innerHTML = "";
}

function checkNumbers() {
for (var i = lower; i < upper; i++) {
if (isPrime(i))
writePrime(i);
}
}

function writePrime(p) {
primesList.innerHTML += "<li>" + p + "</li>";
}

function writeMessage(message) {
var messageBox = document.getElementById("messageBox");
messageBox.innerHTML = message;
}

function isPrime(p) {
if (p < 2)
return false;
for (var b = p - 1; b > 1; b--)
if (GCD(p, b) > 1)
return false;
return true;
}

function GCD(a, b) {
if (b == 0)
return a;
else
return GCD(b, a % b);
}
</script>
</body>

</html>





These are my problems:


  1. When the lower bound is close to the upper bound, and they are both relatively large numbers, no output is produced (for example try inputting 900 as the lower bound and 1000 as the upper bound), even though I know for sure prime numbers exist there, and that my method for generating them works.

  2. The primes should be displayed on the screen as they are generated, and not at the end when they have all been created. If you input 0 as the lower bound and 10000 as the upper bound you will clearly see this issue (I suggest running the snippet full screen and watching the scrollbar size).

  3. This is related to number 2. The
    Generating ...
    message is never shown, only
    Done
    .



EDIT: I made some changes to the code that solved the first issue.

Answer

I would suggest using Promises to break the blocking behaviour of the for loop and give the browser/UI enough time to render the results.

<body>
   <h1>Let's Generate Some Primes!!!</h1>
   <form id="userInput">
      <ul>
         <li>
            <label>Lower Bound (inclusive):</label>
            <input type="number" id="lower">
         </li>
         <li>
            <label>Upper Bound (exclusive):</label>
            <input type="number" id="upper">
         </li>
         <li>
            <input type="submit" value="Generate!">
         </li>
      </ul>
   </form>
   <p id="messageBox"></p>
   <ul id="primes"></ul>
  
   <script>
   var primesList = document.getElementById("primes");
   var lower;
   var upper;
   var NOT_STARTED = "Generating ...";
   var DONE = "Done!";
   window.onload = function() {
      userInput.onsubmit = function(e) {
         e.preventDefault();
         writeMessage(NOT_STARTED);
         if (checkInput()) {
            checkNumbers()
               .then(function() {
                  writeMessage(DONE);
               })
         }
      }
   }

   function checkInput() {
      initializeBounds();
      if (lower >= upper) {
         writeMessage("Lower bound must be less than upper bound.");
         return false;
      }
      return true;
   }

   function initializeBounds() {
      lower = Math.round(document.getElementById("lower").value);
      upper = Math.round(document.getElementById("upper").value);
      primesList.innerHTML = "";
   }

   function checkNumbers() {
      var promise = Promise.resolve();
      for (var i = lower; i < upper; i++) {
         (function(i) {
            promise = promise.then(function() {
               if (isPrime(i))
                  return writePrime(i);
            });
         })(i);
      }
      return promise;
   }

   function writePrime(p) {
      return new Promise(function(resolve, reject) {
         primesList.innerHTML += "<li>" + p + "</li>";
         writeMessage("Generating ..." + Math.round(100 * p / (upper - lower)) + "%");
         setTimeout(function() {
            resolve();
         }, 0);
      });
   }

   function writeMessage(message) {
      var messageBox = document.getElementById("messageBox");
      messageBox.innerHTML = message;
   }

   function isPrime(p) {
      if (p < 2)
         return false;
      for (var b = p - 1; b > 1; b--)
         if (GCD(p, b) > 1)
            return false;
      return true;
   }

   function GCD(a, b) {
      if (b == 0)
         return a;
      else
         return GCD(b, a % b);
   }
   </script>
</body>

External link to jsFiddle.