After the data has been transferred back to the UI thread you once again will see that the length of the ArrayBuffer is zero.
In the UI thread the data can be retrieved in the usual way:
worker.addEventListener("message",
function (event) { console.log("UI after return " + event.data.byteLength);
console.log("UI after return original " + arrayBuf.byteLength);
});
In this case you will see that the event.data is an ArrayBuffer of eight bytes and the original arrayBuf is still zero – that is the original transferred data is not restored.
You can, of course, restore the reference to the original data:
worker.addEventListener("message",
function (event) {
console.log("UI after return " + event.data.byteLength);
arrayBuf=event.data;
console.log("UI after return original " + arrayBuf.byteLength);
});
Now it looks as if the original data has been handed back by the Worker thread.
The big problem with transferring data is that the thread that owned it originally doesn't get the use of it, not even a copy, while the other thread is using it. This doesn't matter as long as the initial owner is generating the data for the first time. For example, if a Worker thread is downloading a resource it can transfer it to the UI thread to be used with no problems. Compare this to a Worker thread that is modifying rather than originating the data. If the UI thread passes the Worker thread a bitmap to process, what does the UI thread show while the worker has ownership?
At the time of writing support for ImageBitmap is restricted to Firefox and Chrome and is best avoided.
Shared and Service Workers
There are other types of Worker object that you can use, but these are more specialized and not as well supported. In particular, Safari dropped support to both Shared and Service workers.
A SharedWorker is another thread that can be accessed from multiple browsing contexts – windows, iframes or other workers. You create a shared worker using:
var worker=new SharedWorker(url);
Communication is via postMessage as per a standard Worker thread, but now we have to use a port object to make the connection. You have to start the port and then you use its postMessage method:
The connection from the SharedWorker back to the UI thread is performed via the onconnect event:
this.addEventListener("onconnect", function(event){
var port=e.ports[0];
...
Once you have the port object you can send messages using it:
port.start();
port.sendMessage(data);
Apart from the need to use a port object to send messages, the only other difference is that a single instance of the Worker code serves all of the other threads making use of it. To take advantage of a Shared Worker's abilities you really need to store the port object as different threads connect to it. By keeping a list of ports, the SharedWorker can pass data from one thread to another and hence one window or iFrame to another. Notice that the one origin rule applies to SharedWorkers and all code and windows have to have come from the same source domain.
SharedWorkers are interesting, but until Apple decides to support them they are probably not a good gamble. ServiceWorkers on the other hand might be. They provide facilities that make the offline experience of using a web app much more like that of a native app. As such some see the ServiceWorker as the future of the web. At first Apple withdrew support for ServiceWorkers in Safari, but it is currently under consideration. The final chapter is devoted to using ServiceWorkers.
Summary
You can create a Worker object which runs on a different thread to the UI thread.
The UI thread doesn't share any objects with the new thread and vice versa.
The Worker thread has access to all of JavaScript and a subset of the global objects available to the UI thread.
Communication from the thread that created the Worker object to the new thread makes use of its postMessage method.
Communication from the Worker thread to the thread that created it also makes use of the postMesssage method, but of the DedicatedWorkerGlobalScope object.
In each case a message event is triggered on the appropriate thread and the data property of the event object provides the data sent as part of the message.
The data transferred to the worker thread and to the UI thread is a copy, a clone, of the original.
By limiting the way the thread can interact, the JavaScript programmer is protected from the main difficulties of multi-threading and we don't have to worry about locks and race conditions.
A big problem with using Worker threads is that communication is event driven, but the Worker thread is at its most simple when just ignoring asynchronous considerations and just gets on with the job.
To allow for responsive communication between the threads both have to be written in an asynchronous style. Any long running function in the Worker thread will mean that it doesn't respond to messages sent to it from the UI thread.
You can use a Blob object and a Data URL to avoid having to use a separate file for the Worker's code.
A faster way to exchange data between threads is to use transferable objects.
As well as dedicated Workers you can also create SharedWorkers which can pass messages between different threads and ServiceWorkers which make implementing web apps easier. Neither are supported by Safari.