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

时间:2018-04-27 19:40:40

标签: javascript event-loop

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

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

我的实现如下:

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;
}

有人可以帮助我实施并给我一个解释/提示以备将来参考吗?

提前致谢。

2 个答案:

答案 0 :(得分:2)

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

有了Web工作人员,您的客户端算法就是:

  • 创建一名工人。
  • 如果您希望它计算素数,请发送消息。
  • 收听一条名为“已完成”的消息,这表示该工作人员已完成。

客户端代码:

// 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的代码。

现在您需要Web worker代码。这是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很容易实现,如果您不想自己编写代码,您会在线找到JavaScript实现。您可以使用Web Storage API保存生成的素数列表并加快将来的计算。

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

答案 1 :(得分:1)

如何在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