40

I currently have this code to create a Web Worker:

w = new Worker("webwork.js");
w.onmessage = function(event) { alert(event.data); }

And then the webwork.js code for the Web Worker:

self.onmessage = function(event) {
    //var ss=r;  //Causes error because of undefined
    var ss="";
    for(var currProp in event) {
        ss+=("event."+currProp+"="+event[currProp]+"\n");
    }
    postMessage(ss);
}

Now I want to transfer a 128-Megabyte ArrayBuffer with this code:

var r = new ArrayBuffer(1048576*128);
w.postMessage(0, [r]);

Now that I have supposedly transferred the variable r, how do I access it from the Web Worker itself. I have tried event.r, just r, self.r and other things like trying to add a second function argument for the array of ArrayBuffers, but nothing works.

How can I access the transferred variable(s) from the Web Worker?

Rob W
  • 341,306
  • 83
  • 791
  • 678
alt.126
  • 1,097
  • 1
  • 9
  • 22
  • Won't it be in `event.data`? – bfavaretto Apr 17 '13 at 22:29
  • Doesn't seem so. If I use "w.postMessage(0,[r]);", then "event.data" is the 0, but I don't know where the array of ArrayBuffers is located at, even by inspecting all of the properties of the event. I can't find it. – alt.126 Apr 17 '13 at 23:19

4 Answers4

87

The answer given by sbr works, but it would cause a copy of the data to be made before being sent to the worker. That could be slow for a large amount of data.
To use "transferable objects" you actually transfer the ownership of the object to or from the web worker. It's like passing by reference where a copy isn't made. The difference between it and the normal pass-by-reference is that the side that transferred the data can no longer access it.

I believe the way you should send the data in your example is:

w.postMessage(r,[r]);  // first arg is r, not 0 as in the question

And the way you would access it in the web worker:

addEventListener('message', function(event) {
    var r = event.data;
});

In my own application I needed to send a large typed Float64Array from the web worker to the main thread, without the performance penalty of the copy. It took lots of trial and error and searching, so I figured I should include that example here for anyone else who gets stuck with a similar problem.
This is the code that worked on the worker side (arr is my Float64Array):

self.postMessage(arr.buffer, [arr.buffer]);

On the receiving main thread I have:

theWorker.addEventListener('message', function(ev) {
    var arr = new Float64Array(ev.data);  // just cast it to the desired type - no copy made
    // ...
});

Note that this works in Chrome, but maybe not most other browsers as of this date (haven't tried yet.)

Also, if you want to send other information in addition to the large array, you can do this:

self.postMessage({foo:"foo", bar:arr.buffer}, [arr.buffer]);

On the receiving (in this example the main) thread:

theWorker.addEventListener('message', function(event) {
    var foo = event.data.foo;
    var arr = new Float64Array(event.data.bar);  // cast it to the desired type
    // ...
});
Lawrence
  • 4,909
  • 1
  • 15
  • 11
  • 5
    This answer is better than the accepted answer because it explains the purpose of the second array parameter much better. – moron4hire Dec 29 '17 at 02:21
  • 2
    Thanks for actually showing how to send copyable data in addition to transferring the buffer. I couldn't find a decent example on MDN anywhere. – General Grievance Oct 04 '22 at 13:56
  • I also couldn't figure this out based on the MDN docs. I opened https://github.com/mdn/content/issues/23099 about making the documentation a bit clearer. – Peter Herdenborg Dec 21 '22 at 11:22
57
postMesage(aMessage, transferList)

In transferList, you must specify the transferable objects that are contained in aMessage:

const objData = {
    strText: "coolcmd",
    objArrayBuffer: new ArrayBuffer(10),
    objTypedArray: new Int8Array(20)
};
objWorker.postMessage(
    objData,
    [objData.objArrayBuffer, objData.objTypedArray.buffer]
);
// The transferred objects are now empty (detached
// from resource). Specifically,
// objData.objArrayBuffer.byteLength returns 0, and
// any access to its content will throw an exception.

In the worker:

self.onmessage = objEvent => {
    console.log(
        objEvent.data.strText,
        objEvent.data.objArrayBuffer,
        objEvent.data.objTypedArray
    );
};

Or using an object destructuring:

self.onmessage = ({data: {strText, objArrayBuffer, objTypedArray}}) => {
    console.log(
        strText,
        objArrayBuffer,
        objTypedArray
    );
};

List of transferable objects.

CoolCmd
  • 939
  • 8
  • 13
  • 2
    Does anyone know if this tranfers ownership, or makes a copy? I tried it and it looks like it transfers ownership. – dooderson Jul 29 '16 at 18:10
  • 1
    Create ArrayBuffer and Int8Array only once! Then keep sending it back and forth. Otherwise using transferable objects makes no sense at all. – Pawel Aug 03 '16 at 08:31
  • 2
    @Pawel can you elaborate how you can keep sending it back and forth? Once you've received the array, if you try to postMessage it again the browser complains with "ArrayBuffer at index 0 is already neutered." – RedShift Jul 11 '17 at 20:51
  • @dooderson A bit of a late answer, but for posterity: Yes, this transfers ownership (https://developer.mozilla.org/en-US/docs/Web/API/Transferable). I assume its to maintain atomicity – Tony Feb 03 '21 at 04:21
  • 1
    @Tony Your link is broken, this one works: https://developer.mozilla.org/en-US/docs/Glossary/Transferable_objects – gouessej Jul 25 '22 at 09:05
-1

Try w.postMessage([0,r]). To use transferable objects, one needs to pass the array buffer as the second item on an array. See this

r90t
  • 366
  • 3
  • 15
Wyatt
  • 2,491
  • 15
  • 12
  • It's the second parameter that needs to be passed and that is considered transferable. Your answer is confusing. – gouessej Jul 25 '22 at 09:07
-5

this works for me :

//in the Main

var x = new ArrayBuffer(1048576*128);
w.postMessage({buffer: x});

// In the worker thread, in message handler,

processMessage: function(ev){
    var buffer = ev.data.buffer,
    // other stuff . buffer is referenced correctly here. 
}
sbr
  • 4,735
  • 5
  • 43
  • 49