Chrome 13 引入了使用称为结构化克隆的算法向 Web Worker 发送 ArrayBuffer
的机制。这样一来,postMessage()
API 就可以接受不仅是字符串的消息,而且还接受 File
、Blob
、ArrayBuffer
和 JSON 对象等复杂类型的消息。更高版本的 Firefox 也支持结构化克隆。
越快越好
结构化克隆很棒,但仍然是一项复制操作。将 32MB 的 ArrayBuffer
传递给 worker 的开销可能为数百毫秒。新版本的浏览器显著提升了消息传递方面的性能,称为“可传输对象”。
借助可传输对象,数据从一个上下文传输到另一个上下文。它是零复制的,极大地提高了向 worker 发送数据的性能。如果您是 C/C++ 用户,可以将其视为传递引用。但是,与按引用传递不同,在转移到新上下文后,调用上下文中的“版本”将不再可用。例如,将 ArrayBuffer
从主应用传输到 worker 时,原始 ArrayBuffer
会被清除,且不再可用。其内容会(静默地)传输到 worker 上下文。
为了尝试使用可转移对象,我们推出了支持可转移对象的新版 postMessage()
:
worker.postMessage(arrayBuffer, [transferableList]);
window.postMessage(arrayBuffer, targetOrigin, [transferableList]);
对于 worker 情形,第一个参数是 ArrayBuffer
消息。第二个参数是应传输的内容的列表。在本例中,您需要在可转移列表中指定 arrayBuffer
。
基准演示
如需了解可转移数据在性能方面的提升,我整理了一份演示。
演示版向 worker 发送 32MB ArrayBuffer
,然后使用 postMessage()
返回。如果您的浏览器不支持可转移数据,则示例会回退到结构化克隆。在不同浏览器中平均运行 5 次,以下是我得到的结果:
在 MacBook Pro/10.6.8/2.53 GHz/Intel Core 2 Duo 上,FF 是使用结构化克隆的最快速度。平均而言,将 32MB 的 ArrayBuffer
发送到 worker 并将其发布回主线程需要 302 毫秒(RRT - 往返时间)。与可传输资源相比,同一测试耗时 6.6 毫秒。性能可大大提升!
这样的速度可以让大量 WebGL 纹理/网格在 worker 和主应用之间无缝传递。
特征检测
对于此问题,特征检测有点棘手。我的建议是向您的工作人员发送一个小型 ArrayBuffer
。如果缓冲区处于传输状态,但未复制,则其 .byteLength
会变为 0:
var ab = new ArrayBuffer(1);
worker.postMessage(ab, [ab]);
if (ab.byteLength) {
alert('Transferables are not supported in your browser!');
} else {
// Transferables are supported.
}
支持:目前支持 Chrome 17+、Firefox、Opera、Safari 和 IE10+
更新日期 (2011-12-13):用于显示 webkitPostMessage()
签名的代码段对于 window 和 worker 不同。
更新日期 (2016-11-03):移除了供应商前缀并更新了代码段