在等待时执行延迟函数所依赖的代码

bon*_*ang 7 javascript settimeout

在下面的最小示例中,延迟替换旧内容setTimeout以便为用户提供完成查看的时间.同时,正在准备新内容,以避免在潜在的昂贵任务期间阻塞用户界面.

var div = document.getElementById('wrapper');
var newContent = document.createElement('ul');

setTimeout(function() {
  var header = div.firstElementChild;
  header.innerHTML = 'New Content';
  header.nextElementSibling.remove();
  div.appendChild(newContent);
}, 2000);

// Make new content while we wait
[1, 10, 100, 1000].forEach(function(x) {
  var li = document.createElement('li');
  li.innerHTML = 'Factorial of ' + x + ' is ' + factorial(x);
  newContent.appendChild(li);
});

function factorial(num) {
  if (num === 0) {
    return 1;
  } else {
    return (num * factorial(num - 1));
  }
}
Run Code Online (Sandbox Code Playgroud)
<div id='wrapper'>
  <h1>Old content</h1>
  <p>Read it before it's gone.</p>
</div>
Run Code Online (Sandbox Code Playgroud)

我对这种方法的关注是,newContent在更换到期时,似乎没有准备就绪.我也不确定这种方法是否会阻止用户界面,或者是否setTimeout将同时执行所使用的任务.

如何确保在执行潜在的昂贵任务时不阻止用户界面并在完成后立即使用它?

小智 9

您长时间运行的计算将阻止浏览器,这绝不是一个好主意.因此,您应该将其放在Web工作者中.

现在,最好用异步工具(如promises)编写异步代码.这是一个通用的伪代码级方法:

// Create a promise which fulfills after some # of ms.
function timeout(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

// Create the worker, kick it off, and 
// return a promise which fulfills when the worker reports a result.
function waitWorker() {
  const worker = new Worker('factorial.js');
  worker.postMessage([1, 10, 100, 1000]);

  return new Promise(resolve => 
    worker.addEventListener('message', event => resolve(event.data))
  );
}

// Wait for both the worker to complete and the two seconds to elapse.
// Then output the data.
Promise.all([timeout(2000), waitWorker()])
  .then(values => output(values[1]);
Run Code Online (Sandbox Code Playgroud)

写作工人留下来作为练习.

使用异步功能

如果您的环境支持异步函数,您还可以使用异步函数更清晰地表达这一点,如下所示:

async function calcAndWait() {
  const result = waitWorker(); // Kick off computation.
  await timeout(ms);           // Wait for two seconds.
  output(await result);        // Wait for computation to finish and output.
}
Run Code Online (Sandbox Code Playgroud)


mab*_*mab 4

你有两个要求:

  1. 至少 2 秒后才可以隐藏介绍。
  2. 在内容准备好之前不要隐藏介绍。

下面的变化满足了这一点。

<html>
<body>
<div id='wrapper'>
  <h1>Old content</h1>
  <p>Read it before it's gone.</p>
</div>
<script>
var div = document.getElementById('wrapper');
var newContent = document.createElement('ul');
var contentReady = false;
var timesUp = false;

function onContentReady() {
  if (! timesUp || ! contentReady) return;
  var header = div.firstElementChild;
  header.innerHTML = 'New Content';
  header.nextElementSibling.remove();
  div.appendChild(newContent);
}

setTimeout(function() { 
    timesUp = true;
    onContentReady();
  } , 2000);

function makeContent() {
    // Make new content while we wait
    [1, 10, 100, 1000].forEach(function(x) {
      var li = document.createElement('li');
      li.innerHTML = 'Factorial of ' + x + ' is ' + factorial(x);
      newContent.appendChild(li);
    });
    contentReady = true;
    onContentReady();
}

function factorial(num) {
  if (num === 0) {
    return 1;
  } else {
    return (num * factorial(num - 1));
  }
}

setTimeout(function() { 
    makeContent();
  } , 4000);
</script>
</body>
</html>
Run Code Online (Sandbox Code Playgroud)

将此代码中的时间值更改为小于 2 秒和大于 2 秒即可看到。

setTimeout(function() { 
    makeContent();
  } , 4000);
Run Code Online (Sandbox Code Playgroud)