使用 setTimeout 分解长 运行 函数以允许事件处理

Breaking up long running function with setTimeout to allow event handling

我必须找到一种方法来实现一个函数,该函数接受一个输入数字和 returns/displays 最接近的小素数或输入本身(如果它是素数)。

但是,输入可以是一个非常大的数字,在这种情况下,内部函数会阻塞事件循环,我必须确保浏览器能够在计算期间处理事件。

我的实现是这样的:

function getClosestPrime(num) {
    return isPrime(num) ? num : getClosestPrime(num-1);
}

function isPrime(num) {
  for (let i = 2; i < num; i++) {
    if(num % i === 0) return false;
    }
  return num !== 1;
}

有人可以帮助我实现并给我一个 explanation/hints 以供将来参考吗?

提前致谢。

在 Promise 中使用 setTimeout 怎么样?

function getClosestPrime(num) {
  return new Promise(resolve => {
    if (isPrime(num)) {
      resolve(num);
    } else {
      setTimeout(() => resolve(getClosestPrime(num-1)), 0);
    }
  })
}

function isPrime(num) {
  for (let i = 2; i < num; i++) {
    if(num % i === 0) return false;
  }
  return num !== 1;
}

setTimeout(() => alert("foo"), 0);     // 1st alerted
getClosestPrime(10000000).then(alert); // 3rd
setTimeout(() => alert("bar"), 0);     // 2nd

因为 JavaScript 是单线程的,所以最好的解决方案是获得第二个线程来为您解决问题。这可以通过使用 Web Workers API.

来实现

有了网络工作者,您的客户端算法很简单:

  • 创建一个工人。
  • 当我们希望它计算质数时,给它发一条消息。
  • 侦听名为 'done' 的消息,这表明工作人员已完成。

客户端代码:

// create the worker
var primeWorker = new Worker('calculate-prime.js');

function doPrimeComputationInWorker(number) {

  function handleWorkerCompletion(message) {
    if (message.data.command == 'done') {
      // update UI using the 'primeNumber' value received in the message 
      console.log(message.data.primeNumber);
      // remove the event listener
      primeWorker.removeEventListener('message', handleWorkerCompletion);
    }
  }

  // add the event listener
  primeWorker.addEventListener('message', handleWorkerCompletion, false);

  // post the number to the worker
  primeWorker.postMessage({
    'number': number
  });
}

代码应该足够简单,您只需更改代码以在工作人员完成时更新 UI。

现在您需要网络工作者代码。这是 calculate-prime.js 的框架:

// add the event listener
self.addEventListener('message', start);

function start(message) {
  // get the number value from the message
  var number = message.data.number;

  // perform the calculation
  var nearestPrime = calculateNearestPrime(number);

  // return the result
  self.postMessage({
    'command': 'done',
    'primeNumber': nearestPrime
  });
}

function calculateNearestPrime(number) {
  // your implementation goes here
  // return the result
  return result;
}

我会把素数的计算留给你,但 Sieve of Eratosthenes is quite easy to implement and you will find JavaScript implementations online if you don't want to write the code yourself. You could use the Web Storage API 保存生成的素数列表并加快以后的计算。

为了将来参考,您应该了解 Promises and Generators 以便您可以使用它们来解决您的异步问题。