跳到主要内容

与 Web Workers 通信

使用后台线程时不能访问页面或窗口对象,但是并不代表后台线程不能与页面之间进行数据交互。为了实现页面与 Web Workers 通信,可以调用 postMessage 函数传入所需数据,同时将建立一个监听器,用来监听由 Web Workers 发送到页面的消息。

为建立页面和 Web Workers 之间的通信,首先在页面中添加对 postMessage 函数的调用,如下所示。

document.getElementById('helloButton').onclick = function () {
worker.postMessage('Hi,How are you');
};

当用户单击按钮后,相应信息会被发送给 Web Workers ,然后将事件监听器添加到页面中,用来监听从 Web Workers 发来的信息。

worker.addEventListener('message', messageHandler, true);
function messageHandler(e) {
//process message from worker
}

编写 HTML5 Web Workers JavaScript 文件。在该文件中,需要添加事件监听器以监听发来的消息,并且通过调用 postMessage 函数实现与页面之间的通信。

为了完成页面与 Web Workers 之间的通信功能。首先,添加代码调用 postMessage 函数。例如,在 messageHandler 函数中可以添加如下代码。

function messageHandler(e) {
postMessage('worker says: ' + e.data + ' too');
}

接下来,在 Web Workers JavaScript 文件中添加事件监听器,以处理从页面发来的信息:

addEventListener('message', messageHandler, true);

接收到信息后会马上调用 messageHandler 函数以保证信息能及时返回。

通过 postMessage 函数将对象传递到 Worker 或者从中返回对象,这些对象将被自动转换为 JSON 格式。

var onmessage = function (e) {
postMessage(e.data);
};

注意:在 Worker 中进出的对象不能包含函数和循环引用,因为 JSON 不支持它们。

在 Web Workers 脚本中如果发生未处理的错误,会引发 Web Workers 对象的错误事件。特别是在调试用到 Web Workers 脚本时,对错误事件的监听就显得尤为重要。下面显示的是 Web Workers JavaScript 文件中的错误处理函数,它将错误记录在控制台上。

function errorHandler(e) {
console.log(e.message, e);
}

为了处理错误,还必须在主页上添加一个事件监听器:

worker.addEventListener('error', errorHandler, true);

当 Worker 发生运行时错误时,它的 onerror 事件就会被触发。该事件接收一个 error 的事件,该事件不会冒泡,并且可以取消。要取消该事件可以使用 preventDefault() 方法。该错误事件有以下 3 个属性。

  • message :可读的错误信息
  • filename :发生错误的脚本文件名称
  • lineno :发生错误的脚本所在文件的行数

Web Workers 不能自行终止,但能够被启用它们的页面所终止。调用 terminate 函数可以终止后台进程。被终止的 Web Workers 将不再响应任何信息或者执行任何其它计算。终止之后, Worker 不能被重新启动,但可以使用同样的 URL 创建一个新的 Worker 。

worker.terminate();

如果需要马上终止一个正在运行中的 Worker ,则可以调用它的 terminate() 方法:

myWorker.terminate();

这样一个 Worker 进程就被结束了。

在多线程之间通信

  • 先创建发送数据的子线程
  • 执行子线程中的任务,然后把要传递的数据发送给主线程
  • 在主线程接收到子线程传回来的消息时,创建接收数据的子线程,然后把发送数据的子线程中返回的消息传递给接收数据的子线程
  • 执行接收数据子线程中的代码

完成主页面的设计。包括 HTML 结构和 CSS 样式。在主页脚本中创建一个主线程,定义请求数据为空,在主线程响应事件 onmessage 回调函数中处理后台返回的处理数据,并把它们显示在页面中。

修改主线程中的代码。在主线程中定义一个子线程(发送数据),让其随机生成 200 个数字,并返回这个随机数组。在该子线程的回调函数中再定义一个子线程(接收数据),把接收到的随机数组传递给它,并接收该线程过滤后的数组。

发送数据的子线程中创建了一个 200 个整数构成的随机数组。然后把它转换为字符串并返回,最后关闭该子线程。

接收数据子线程中对接收到的随机数组中挑选能被 3 整除的数字,然后拼接成字符串并返回。

使用多线程绘图

JavaScript 一个最原始的缺陷就是它的单线程任务处理,这个局限性意味着一个长时间运行的进程会冻结主窗口。通常说的浏览器 UI 线程阻塞,这是由于主线程在处理所有的可视化元素及其相关任务,如绘制、刷新、动画、用户输入事件等。

这种线程过载的严重后果是页面被冻结,且用户不能再与 Web 应用进行交互。因此, Web 应用的用户体验就非常差,用户可能会关掉这个 Tab 或者整个浏览器。为了避免发生这种情况,浏览器已经引入一种保护机制,当脚本运行时间过长时,就会弹出警告。

但是,这种机制并不能正确分辨究竟是一段脚本编写有问题,还是这段脚本确实需要更多的时间来完成它的工作。尽管如此,由于它阻塞了 UI 线程,所以提示一下比让用户无限等待要好。

为了避免出现此类情况,传统做法是通过 setTimeout 和 setInterval 方法试图模拟并行任务,或者通过 XMLHttpRequest 对象,也可以异步地处理 HTTP 请求,避免从远程服务器载入资源时冻结 UI 。当然,有了 Web Workers 技术之后,情况会有很大的好转。