JavaScript Async - Basic Worker |
Written by Ian Elliot | |||||
Monday, 17 April 2023 | |||||
Page 2 of 4
The Trouble With ThreadsIf you have looked at the problem of writing multi-threaded programs, and all of the problems that this creates, this is where you might be getting worried. In general multi-threaded programs are difficult to get right. Starting a new thread so easily seems to be an easy way to do something dangerous. However, Web Workers have been implemented in a way that restricts the way that you use them to make them safe. At first these restrictions might seem tough to live with, but after a while you realize that they are perfectly reasonable and don't really stop you from doing anything. The main simplification about threading with Web Workers is that the new thread cannot share anything with the UI thread. The new thread cannot access any objects that the UI Thread can. This means it cannot interact with any global objects and it cannot interact with the DOM or the user interface in any way. The Worker thread is isolated from the UI thread and cannot access any objects that are created by the UI thread and vice versa. The new thread runs in a little world of its own, but don't panic as it can communicate with the UI thread in a very simple way. This inability to share objects may seem a little restrictive, but it is a restriction that is necessary to make sure that the two threads you now have don't try to access the same object at the same time. If this was allowed to happen you would need to introduce a lot of complicated machinery – locks, semaphores and so on – to make sure that the access was orderly and didn't give rise to any very difficult to find bugs – race conditions, deadlock and so on. The problem is that if you have multiple threads accessing the same data you can't be sure of the order that things are happening in. Suppose one thread is in the middle of updating a shared object and a second thread deletes it in the middle of the update. This would at best create a program that didn't do what the user expected and at worst would generate a runtime error. The only way to stop this from happening is to use mechanisms that restrict access to shared objects so that only one thread can be working with it at any given time. This is potentially very complex and very difficult to prove that you have things correct. The alternative to managing shared objects in this way is to ban shared objects. In this case there is absolutely no danger of a simultaneous access to any object, but this makes interaction impossible. In other words the Web Worker has big restrictions so that you can use it without complication and without any danger. For most purposes it is sufficient and hence very effective. Web Workers do have access to all of the global core JavaScript objects that you might expect, but they are not shared. They can also access some functions that are normally associated with the DOM – XMLHttpRequest() and setInterval etc. The rule is that if the object is unique to the UI thread or could be shared in any way with the UI thread then you cannot get access to it and this is a condition that is obviously satisfied for all of the core JavaScript objects and the few DOM derived objects that are allowed. To make up for this restriction there are two new objects that the Web Worker can access - WorkerNavigator and WorkerLocation. The navigator provides basic information about the app's context - the browser in use, appName, appVersion and so on. The location object provides details of where the app is in terms of the current URL. If these two objects don't provide enough information you can easily arrange to pass the Worker thread additional data of your choosing. Basic Communication MethodsSo if the Web Worker is isolated from the UI thread, how do the two communicate? The answer is that they both use events and a method that causes events on the other thread. UI Thread To Worker ThreadLet's start with the UI thread sending a message to the Worker thread. The Worker object that is created on the UI thread has a postMessage method which triggers a message event on the Worker thread. This is similar to the window.postMessage method that we have already used to implement custom events and asynchronous methods. Notice that this is where the thread crossover occurs. The Worker object is running on the UI thread, but the event occurs in the code running on the Worker thread. Notice that the Worker thread has an event queue all of its own – it is a complete event-driven program of its own. For example: var worker=new Worker("myScript.js"); worker.postMessage({mydata:"some data"}); The postMessage method triggers a message event in the worker code and sends it an event object that includes the data object as its data property. That is, event.data has a mydata property equal to "some data". To get the message sent to the worker you have to set up an event handler and retrieve the event object's data property. For example, in the Web Worker code you would write: this.addEventListener("message", function (event) { In the Web Worker code the global context is provided by this or self and this gives access to all of the methods and objects documented. To get the message you would use: var somedata = event.data.mydata; Of course as you are passing an object to the event handler you could package as many data items as you needed to. Notice that it is important to be very clear what is going on here. The postMessage method call is on the UI thread, but the event handler is on the Worker thread. It is also important to realize that the data that is passed between the two threads isn't shared. A copy is made using the structured clone algorithm and it is this copy that the worker received. You can use a wide range of types of data to pass to the worker, but if it is big the time taken to copy could be significant. If this is the case. you need to use a transferable object which is shared rather than copied – see later. |
|||||
Last Updated ( Wednesday, 19 April 2023 ) |