长时间运行的脚本

你可能注意到过,有时候浏览器会提示脚本运行时间过长,询问用户是否要停止执行。不管应用有多复杂,你都不希望这种情况发生在自己的应用中。

同时,如果脚本运行时间太长的话,浏览器的UI将变得没有响应,用户不能点击任何东西。这是一种很差的用户体验,应该尽量避免。

在JavaScript中没有线程,但你可以在浏览器中使用setTimeout()来模拟,或者在现代浏览器中使用web workers。

setTimeout()

它的思想是将一大堆工作分解成为一小段一小段,然后每隔1毫秒运行一段。使用1毫秒的延迟会导致整个任务完成得更慢,但是用户界面会保持可响应状态,用户会觉得浏览器没有失控,觉得更舒服。

1毫秒(甚至0毫秒)的延迟执行命令在实际运行的时候会延迟更多,这取决于浏览器和操作系统。设定0毫秒的延迟并不意味着马上执行,而是指“尽快执行”。比如,在IE中,最短的延迟是15毫秒。

Web Workers

现代浏览器为长时间运行的脚本提供了另一种解决方案:web workers。web workers在浏览器内部提供了后台线程支持,你可以将计算量很大的部分放到一个单独的文件中,比如my_web_worker.js,然后从主程序(页面)中这样调用它:

  1. var ww = new Worker('my_web_worker.js');
  2. ww.onmessage = function (event) {
  3. document.body.innerHTML +=
  4. "<p>message from the background thread: " + event.data + "</p>";
  5. };

下面展示了一个做1亿次简单的数学运算的web worker:

  1. var end = 1e8, tmp = 1;
  2. postMessage('hello there');
  3. while (end) {
  4. end -= 1;
  5. tmp += end;
  6. if (end === 5e7) { // 5e7是1e8的一半
  7. postMessage('halfway there, `tmp` is now ' + tmp);
  8. }
  9. }
  10. postMessage('all done');

web worker使用postMessage()来和调用它的程序通讯,调用者通过onmessage事件来接受更新。onmessage事件处理函数接受一个事件对象作为参数,这个对象含有一个由web worker传过来data属性。类似的,调用者(在这个例子中)也可以使用ww.postMessage()来给web worker传递数据,web worker可以通过一个onmessage事件处理函数来接受这些数据。

上面的例子会在浏览器中打印出:

  1. message from the background thread: hello there
  2. message from the background thread: halfway there, `tmp` is now 3749999975000001 message from the background thread: all done