JavaScript Canvas - Web Workers
Written by Ian Elliot   
Tuesday, 07 June 2022
Article Index
JavaScript Canvas - Web Workers
The Trouble With Threads
Transferable Objects

Transferable Objects

The way that worker threads avoid many of the problems of multi-threading is by limiting the way threads can interact. They can only communicate by sending messages and any data that is included in the message isn't shared, but copied. This means that each thread is always working on its own data and no object can be used by more than one thread at a time.

Copying data is safe, but if the data is large it can be slow. An alternative is to use transferable objects. These are not copied, but even so only one thread can access them at any given time. The idea is that the object belongs to just one thread and only that thread can access the object. Ownership of the thread can be passed to another thread by the object being included in a message sent from the current owner to another thread.

Transferable objects are only supported in the latest browsers. The mechanism only applies to four types of object - ArrayBuffer, MessagePort, ImageBitMap and OffscreenCanvas. This makes sense because these are all potentially large objects which are best not passed by copy.

To send a transferable object you simply follow the usual data object in postMessage by an array of transferable objects.

The way that transferable objects work is simple, but it can be confusing. As usual, you pass data using the message object which becomes the data property of the event object passed to the event handler. The data that you want to transfer has to be included either as the message object or a property of the message object.

To be passed by transfer rather than clone, the object also has to be listed in the transfer array. So, for example:

postMessage(object,[object]);

will transfer object and the event handler will receive the object as event.data.

Alternatively, you could use:

postMessage({mydata: object},[object]);

which will also transfer object, but the event handler will receive it as event.data.mydata. Obviously, you could pass additional properties and if these were not included in the transfer list they would be passed by cloning.

Once an object has been transferred it is no longer usable in the original thread. For example in the case of an ArrayBuffer its size is reduced to zero in the original thread. Notice that once transferred its ownership cannot be simply transferred back because the reference that is passed is in event.data or event.data.myobject and not the original reference. That is, it is not a simple turning off and on of the original reference.

You can arrange for this to happen, however. We need a very simple example and to do this we need to work with an ArrayBuffer. An ArrayBuffer is a raw collection of bytes and you cannot access it directly. It has to be converted into a typed array before you can access its data, see Chapter 11 for details. However, we can simply create a raw buffer and pass it back and forth between the main thread and the worker thread as an example without worrying about what data it contains:

var worker = new Worker("transfer.js");
var arrayBuf = new ArrayBuffer(8);
console.log("UI before transfer " + arrayBuf.byteLength);
worker.postMessage(arrayBuf, [arrayBuf]);
console.log("UI after transfer" + arrayBuf.byteLength);

This simply creates an eight byte ArrayBuffer and transfers it to the worker thread. When the program is run you see:

UI before transfer 8
UI after transfer 0

indicating that the ArrayBuffer is no longer available in the UI thread.

In the Worker thread we can use the ArrayBuffer in the standard way:

this.addEventListener("message",
      function(event){
       console.log("Worker got data! " +
event.data.byteLength);
});

You will see the message:

Worker got data! 8

indicating that the ArrayBuffer is now available in the worker thread.

The web worker can pass the ArrayBuffer back to the UI thread:

this.addEventListener("message",function(event){
console.log("Worker got data! " +
event.data.byteLength);
postMessage(event.data,[event.data]);
console.log("Worker sent data! " +
event.data.byteLength);
});

After the data has been transferred back to the UI thread you will once again see that the length of the ArrayBuffer is 0.

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 0, 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 or an OffscreenCanvas to process, what does the UI thread show while the worker has ownership?

In book but not in this extract

  • OffscreenCanvas
  • OffscreenCanvas in the UI Thread
  • OffscreenCanvas in the Worker Thread
  • A Worker Animation Example
  • TransferControlToOffScreen

Summary

  • A web worker can run JavaScript code using a separate thread from the UI thread.

  • The web worker’s code is isolated from any code running in the UI thread and in any other thread. It is also unable to access the DOM and other objects that are freely available to the UI thread.

  • Communication between the UI thread and the worker thread is via events and event handlers.

  • Data cannot be simply shared between the threads. Instead you can send a copy of the data as part of the event or you can transfer ownership of a limited number of objects using the transferable object protocol.

  • The OffscreenCanvas object has all of the functionality of a canvas object but it is not part of the DOM and can be used by the UI thread or a Worker thread.

  • OffscreenCanvas is a transferable object and this allows the Worker thread to update a canvas object in the UI thread.

  • This connection between a canvas in the UI thread and an OffscreenCanvas in the Worker thread is so common that the transferControlToOffscreen method will connect the UI thread to an OffscreenCanvas. The UI thread will be automatically updated to show the current content of the OffscreenCanvas.

 

Now available as a paperback or ebook from Amazon.

JavaScript Bitmap Graphics
With Canvas

largecover360

 

Contents

  1. JavaScript Graphics
  2. Getting Started With Canvas
  3. Drawing Paths
      Extract: Basic Paths
      Extract: SVG Paths
      Extract: Bezier Curves
  4. Stroke and Fill
      Extract: Stroke Properties 
      Extract: Fill and Holes
      Extract: Gradient & Pattern Fills
  5. Transformations
      Extract: Transformations
      Extract: Custom Coordinates 
      Extract  Graphics State
  6. Text
      Extract: Text, Typography & SVG 
      Extract: Unicode
  7. Clipping, Compositing and Effects
      Extract: Clipping & Basic Compositing
  8. Generating Bitmaps
      Extract:  Introduction To Bitmaps
      Extract :  Animation 
  9. WebWorkers & OffscreenCanvas
      Extract: Web Workers
      Extract: OffscreenCanvas
  10. Bit Manipulation In JavaScript
      Extract: Bit Manipulation
  11. Typed Arrays
      Extract: Typed Arrays 
  12. Files, blobs, URLs & Fetch
      Extract: Blobs & Files
      Extract: Read/Writing Local Files
      Extract: Fetch API **NEW!
  13. Image Processing
      Extract: ImageData
      Extract:The Filter API
  14. 3D WebGL
      Extract: WebGL 3D
  15. 2D WebGL
    Extract: WebGL Convolutions

<ASIN:B07XJQDS4Z>

<ASIN:1871962579>

<ASIN:1871962560>

To be informed about new articles on I Programmer, sign up for our weekly newsletter, subscribe to the RSS feed and follow us on Twitter, Facebook or Linkedin.

espbook

 

Comments




or email your comment to: comments@i-programmer.info



Last Updated ( Tuesday, 07 June 2022 )