worker.postMessage中的transfer参数

2,741 阅读2分钟

上篇文章web worker涉及到postMessage,在web worker 中可以进行数据传输。更高级用法可以转移对象,并表示很好奇,觉得以后应该会有用,所以打算深入了解下。

1、重点转移对象参数transfer定义

worker.postMessage(message) 
worker.postMessage(message, transfer)
  • 其中message可以是文本,也可以是对象。需要注意的是,这种通信是拷贝关系,即是传值而不是传址,Worker 对通信内容的修改,不会影响到主线程。事实上,浏览器内部的运行机制是,先将通信内容串行化,然后把串行化后的字符串发给 Worker,后者再将它还原。主线程与 Worker 之间也可以交换二进制数据,比如 File、Blob、ArrayBuffer 等类型,也可以在线程之间发送,但是一旦二级制数据量太大,会导致性能问题。
  • transfer可转移对象是如ArrayBufferMessagePortImageBitmap等二进制数据。JavaScript 允许主线程把二进制数据直接转移给子线程,但是一旦转移,主线程就无法再使用这些二进制数据了,这是为了防止出现多个线程同时修改数据的麻烦局面。这种转移数据的方法,叫做Transferable Objects。这使得主线程可以快速把数据交给 Worker,对于影像处理、声音处理、3D 运算等就非常方便了,不会产生性能负担。

2、案例

单个参数使用,输出结果如下:

<script type="text/plain" id="workerJS">
        self.addEventListener('message', function(event) {
            var ab = event.data;
            postMessage('worker线程: ab.byteLength = ' + ab.byteLength);
        });
    </script>
<script>
    let workerJS = document.getElementById('workerJS').innerText;
    let blob = new Blob([workerJS]);
    let blobURL = window.URL.createObjectURL(blob);
    const worker = new Worker(blobURL);

    var ab = new ArrayBuffer(1000000000);
    for (var i = 0; i < ab.length; ++i) {
            ab[i] = i * 2; 
    }
    worker.postMessage(ab);
    console.log("主线程 abbyteLength = ", ab.byteLength);
    worker.addEventListener('message', function (event) {
        console.log(event.data);
    });
//主线程 ab.byteLength =  1000000000
//worker线程: ab.byteLength = 1000000000
</script>

两个参数使用,结果如下:

<script type="text/plain" id="workerJS">
        self.addEventListener('message', function(event) {
            var ab = event.data;
            postMessage('worker线程: ab.byteLength = ' + ab.byteLength);
        });
    </script>
<script>
    let workerJS = document.getElementById('workerJS').innerText;
    let blob = new Blob([workerJS]);
    let blobURL = window.URL.createObjectURL(blob);
    const worker = new Worker(blobURL);

    var ab = new ArrayBuffer(1000000000);
    for (var i = 0; i < ab.length; ++i) {
            ab[i] = i * 2; 
    }
    worker.postMessage(ab,[ab]);
    console.log("主线程 abbyteLength = ", ab.byteLength);
    worker.addEventListener('message', function (event) {
        console.log(event.data);
    });
//主线程 ab.byteLength =  0
//worker线程: ab.byteLength = 1000000000
</script>

3、结论

经过简单的数据对比,发现了数据进行了转移了。 同时观察内存使用率,确实发现使用转移对象,对于内存使用率不会怎么增加。 而使用序列化的,随着数据量越来越多,内存使用情况就会越高了,同时需要把worker关闭了,内存才会明显下降。 说明没使用情况下,一直开着worker对于性能也是一种消耗。