new Worker('worker-script.js')
postMessage()
function is used by the transmitting side.message
event handler is applied on the receiving side.message
event handler takes an event argument in the same way as other handlers. This argument has a data
property that contains the data passed to the receiving side.postMessage()
function to send messages to the worker. A worker can send responses to the main thread using the postMessage()
implementation, which is globally available in the worker's environment. var worker = new Worker("demo1-hello-world.js"); // , postMessage() - worker.onmessage = (evt) => { console.log("Message posted from webworker: " + evt.data); } // - worker.postMessage({data: "123456789"});
// demo1-hello-world.js postMessage('Worker running'); onmessage = (evt) => { postMessage("Worker received data: " + JSON.stringify(evt.data)); };
Message posted from webworker: Worker running Message posted from webworker: Worker received data: {"data":"123456789"}
postMessage()
function.window
and document
objects.console
object for logging messages to the developer console, and the XMLHttpRequest
object for performing AJAX requests. However, in other matters, it is expected that the code executed by the worker is self-sufficient. So, for example, the data from the worker's thread, which is planned to be used in the main thread, should be transferred as a data
object via the postMessage()
function.postMessage()
function is copied, that is, changes to this data made by the main thread will not affect the original data that is in the worker's thread. This is an internal defense mechanism against conflicting parallel data changes that are transmitted between the main stream and the worker thread.n
balls, you need to check the position of each of them in comparison with all the others in order to understand if they do not intersect, and do not need them to change the direction of motion, realizing a rebound, which leads to the number of operations equal to n in the square .n
with a ball m
, then it is no longer necessary to check the collision of a ball m
with a ball n
, however, in spite of this, a large amount of computations is required to solve such a problem).requestAnimationFrame()
call. Here we describe the World
object, which contains a list of Ball
objects. Each Ball
object stores information about its current position and speed (there is also information about the radius and color of the object, which allow you to display it on the screen).Canvas
object and its drawing context). Ball positions are updated in the web worker thread. The speed (in particular, the direction of movement of the balls) changes if they collide with the boundaries of the playing field or with other balls.World
object is passed between the client code in the browser and the worker thread. This is a relatively small object, even for a few hundred balls (for example, for 100 balls, given that you need about 64 bytes of data per one, the total volume will be about 6400 bytes). As a result, the main problem here is not the transfer of data about game objects, but the computational load on the system.Ball
class, which is used to represent animated objects, and the World
class, which implements the move()
and draw()
methods, which perform animation. const canvas = $('#democanvas').get(0), canvasBounds = {'left': 0, 'right': canvas.width, 'top': 0, 'bottom': canvas.height}, ctx = canvas.getContext('2d'); const numberOfBalls = 150, ballRadius = 15, maxVelocity = 10; // World const world = new World(canvasBounds), '#FFFF00', '#FF00FF', '#00FFFF']; // Ball World for(let i=0; i < numberOfBalls; i++) { world.addObject(new Ball(ballRadius, colors[i % colors.length]) .setRandomLocation(canvasBounds) .setRandomVelocity(maxVelocity)); } ... // function animationStep() { world.move(); world.draw(ctx); requestAnimationFrame(animationStep); } animationStep();
requestAnimationFrame()
to call the animationStep()
function 60 times per second, during the screen refresh period. An animation step consists of calling the move()
method, which updates the position of each ball (and, possibly, the direction of its movement), and from the call to the draw()
method, which outputs, using the objects of the canvas
object, balls in new positions.move()
method, that is, the code from World.move()
, must be moved to the worker. The World
object will be passed, in the form of a data
object, to the worker's thread, using the postMessage()
call, which will allow you to call the move()
method here. Obviously, you need to transfer between the main thread and the web worker at the World
object, since it contains a list of Ball
objects displayed on the screen and data about the rectangular area within which they should remain. At the same time, Ball
objects contain all information about the position, speed, and direction of movement of the respective balls. let worker = new Worker('collider-worker.js'); // draw worker.addEventListener("message", (evt) => { if ( evt.data.message === "draw") { world = evt.data.world; world.draw(ctx); requestAnimationFrame(animationStep); } }); // function animationStep() { worker.postMessage(world); // world.move() in worker } animationStep();
// collider-worker.js importScripts("collider.js"); this.addEventListener("message", function(evt) { var world = evt.data; world.move(); // , this.postMessage({message: "draw", world: world}); });
World
object passed to it using postMessage()
from the main thread, and then passes the same object back to the main thread, having previously calculated the new values ​​for the position and speed of the game objects. of the world. Remember that the browser makes a copy of this object when it is transmitted between threads. Here we proceed from the assumption that the time required to create a copy of the World
object is much less than O (n ** n), that is, the time required for collision detection (in fact, a relatively small amount of data is stored in the World
object ). Uncaught TypeError: world.move is not a function at collider-worker.js:10
postMessage()
function, the data of the object's properties is copied, but not its prototype. The methods of the World
object are separated from the prototype when the object is copied and passed to the worker. This is part of the structure cloning algorithm , a standard way of copying objects when transferring them between the main thread and the web worker. This process is also known as serialization .World
class a method for creating its new instance (which will have a prototype with methods) and reassigning the properties of this object based on the data passed using postMessage()
: static restoreFromData(data) { // , let world = new World(data.bounds); world.displayList = data.displayList; return world; }
Ball
objects that World
stores also needs to be restored: Uncaught TypeError: obj1.getRadius is not a function at World.checkForCollisions (collider.js:60) at World.move (collider.js:36)
World
class implementation needs to be expanded in order to restore each Ball
object based on the data passed to postMessage()
, just like the World
class itself is being restored.World
class will look like this: static restoreFromData(data) { // , let world = new World(data.bounds); world.animationStep = data.animationStep; world.displayList = []; data.displayList.forEach((obj) => { // Ball let ball = Ball.restoreFromData(obj); world.displayList.push(ball); }); return world; }
restoreFromData()
method is restoreFromData()
implemented in the Ball
class: static restoreFromData(data) { // , const ball = new Ball(data.radius, data.color); ball.position = data.position; ball.velocity = data.velocity; return ball; }
canvas
object and transforms it, creating another image based on it. Filters.threshold = function(pixels, threshold) { var d = pixels.data; for (var i=0; i < d.length; i+=4) { var r = d[i]; var g = d[i+1]; var b = d[i+2]; var v = (0.2126*r + 0.7152*g + 0.0722*b >= threshold) ? 255 : 0; d[i] = d[i+1] = d[i+2] = v } return pixels; };
postMessage()
function can be used by setting one or more properties that describe the data that is passed in the message by reference. That is, not copies of the data are transferred, but links to them. It looks like this: <div style="margin: 50px 100px"> <img id="original" src="images/flmansion.jpg" width="500" height="375"> <canvas id="output" width="500" height="375" style="border: 1px solid;"></canvas> </div> ... <script type="text/javascript"> const image = document.getElementById('original'); ... // HTML5 canvas const tempCanvas = document.createElement('canvas'), tempCtx = tempCanvas.getContext('2d'); tempCanvas.width = image.width; tempCanvas.height = image.height; tempCtx.drawImage(image, 0, 0, image.width, image.height); const imageDataObj = tempCtx.getImageData(0, 0, image.width, image.height); ... worker.addEventListener('message', (evt) => { console.log("Received data back from worker"); const results = evt.data; ctx.putImageData(results.newImageObj, 0, 0); }); worker.postMessage(imageDataObj, [imageDataObj.data.buffer]); </script>
Transferable
interface. The design of the data.buffer
object meets this requirement - it is of type Uint8ClampedArray
(arrays of this type are designed to store 8-bit image data). ImageData
is what the getImageData()
method returns for the HTML5 canvas
object context
.Transferable
interface implements several standard data types: ArrayBuffer
, MessagePort
, and ImageBitmap
. ArrayBuffer
, in turn, is represented by a number of array types: Int8Array
, Uint8Array
, Uint8ClampedArray
, Int16Array
, Uint16Array
, Int32Array
, Uint32Array
, Float32Array
, Float64Array
.postMessage()
, ( «neutered»). postMessage()
-, . JS-.postMessage()
message
.postMessage()
.postMessage()
, . , .getImageData()
, buffer
postMessage()
( imageData.data.buffer
, imageData.data
). Transferable
.if (typeof Worker !== "undefined")
. , - , , , , ( - requestAnimationFrame()
).Source: https://habr.com/ru/post/352828/
All Articles