在JavaScript中给出意外行为的函数

时间:2016-10-08 19:16:37

标签: javascript html dom

我正在编写一个用JavaScript生成素数的程序。问题是程序的行为很奇怪,似乎函数没有按顺序执行,导致它无法正常执行。这是我的代码:

<!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>

这些是我的问题:

  1. 当下限接近上限时,它们都是相对较大的数字,没有产生输出(例如尝试输入900作为下限和1000作为上限),即使我知道肯定存在素数,并且我生成它们的方法也有效。

  2. 素数应该在生成时显示在屏幕上,而不是在它们全部创建时显示在最后。如果您输入0作为下限,10000作为上限,您将清楚地看到此问题(我建议运行整个片段并观察滚动条大小)。

  3. 这与数字2相关。Generating ...消息永远不会显示,只会显示Done

  4. 编辑:我对解决第一个问题的代码做了一些更改。

2 个答案:

答案 0 :(得分:1)

我建议使用Promises来打破for循环的阻塞行为,并为浏览器/ UI提供足够的时间来呈现结果。

&#13;
&#13;
<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>
&#13;
&#13;
&#13;

jsFiddle的外部链接。

答案 1 :(得分:0)

1 :使用parseInt()将输入(字符串)正确转换为数字。

2,3 for循环阻塞了窗口线程,因此这就是您没有看到逐步结果的原因;只有在代码完成时才会刷新DOM。实际上正在设置该消息,但由于该进程被阻止,您无法看到它。

我已使用setTimeout()功能对您的代码段进行了编辑,该功能会在通话前等待一段时间。

    <!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="progress"></p>
        <ul id="primes"></ul>

        <script>
        var primesList = document.getElementById("primes");
        var lower;
        var upper;
        var NOT_STARTED = false;
        var DONE = true;
        window.onload = function() {
            userInput.onsubmit = function(e) {
                e.preventDefault();

                /* 3: let's give some time for message */
                writeProgressMessage(NOT_STARTED);
                initializeBounds();

                window.setTimeout(function () {
                    checkNumbers(lower, upper);
                }, 1000); // waiting for 1s to start
            }
        }

        function initializeBounds() {
            /* correctly convert the input's for numbers, using parseInt */
            lower = parseInt(document.getElementById("lower").value, 10);
            upper = parseInt(document.getElementById("upper").value, 10);
            primesList.innerHTML = "";
        }

        function checkNumbers(current, upper) {
            if (isPrime(current))
                writePrime(current);

            /* not yet finished */
            if (current != upper) {
                setTimeout(function () {
                    checkNumbers(current+1, upper);
                }, 100); // wait 100ms for the next check
            }
        }

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

        function writeProgressMessage(done) {
            var progress = document.getElementById("progress");
            if (!done)
                progress.innerHTML = "Generating ...";
            else
                progress.innerHTML = "Done!";
        }

        function isPrime(p) {
            if (p < 2 || !Number.isInteger(p))
                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>