乐趣区

关于settimeout:使用-setTimeout-拆解一些-CPU-密集型的执行任务

未优化之前的版本:

let i = 0;

let start = Date.now();

function count() {

  // do a heavy job
  for (let j = 0; j < 1e9; j++) {i++;}

  alert("Done in" + (Date.now() - start) + 'ms');
}

count();

上述 count 函数里的 for 循环的 i 累加,是一个 CPU 密集型工作,在执行结束之前,JavaScript 引擎工作队列里的其余工作,没有机会失去执行。

优化版本

将 i 从 1 累加到 1e9 的工作,拆解成 1000 个小的子工作。每个子工作执行结束之后,调用 setTimeout 调度本身,这样工作队列里其余工作有机会失去执行。

let i = 0;

let start = Date.now();

function count() {// do a piece of the heavy job (*)
  do {i++;} while (i % 1e6 != 0);

  if (i == 1e9) {alert("Done in" + (Date.now() - start) + 'ms');
  } else {setTimeout(count); // schedule the new call (**)
  }

}

count();
  • 第一轮工作执行:i=1…1000000
  • 第二轮工作执行:i=1000001..2000000

以此类推。

当初,如果在引擎忙于执行第 1 局部时呈现新的辅助工作(例如 onclick 事件),它会排队,而后在第 1 局部实现时执行,而后再执行下一部分。在 count 执行之间,应用 setTimeout 定期返回事件循环,为 JavaScript 引擎提供了调度执行其余工作的机会,以对其余用户操作做出反馈。

let i = 0;

let start = Date.now();

function count() {

  // move the scheduling to the beginning
  if (i < 1e9 - 1e6) {setTimeout(count); // schedule the new call
  }

  do {i++;} while (i % 1e6 != 0);

  if (i == 1e9) {alert("Done in" + (Date.now() - start) + 'ms');
  }

}

count();

为浏览器脚本拆分 CPU 密集型工作的另一个益处是咱们能够显示进度批示。

如前所述,只有在以后运行的工作实现后才会绘制对 DOM 的更改,无论以后运行的工作须要多长时间能力执行结束。

上面是一个例子:

<div id="progress"></div>

<script>

  function count() {for (let i = 0; i < 1e6; i++) {
      i++;
      progress.innerHTML = i;
    }
  }

  count();
</script>

对 i 的更改要等到函数执行实现后才会显示,所以咱们只会看到最初一个值。

但咱们也可能想在工作期间展现一些货色,例如一个进度条。

如果咱们应用 setTimeout 将沉重的工作拆分为多个局部,那么 i 会在多个局部执行之间绘制进去。

<div id="progress"></div>

<script>
  let i = 0;

  function count() {// do a piece of the heavy job (*)
    do {
      i++;
      progress.innerHTML = i;
    } while (i % 1e3 != 0);

    if (i < 1e7) {setTimeout(count);
    }

  }

  count();
</script>
退出移动版