Javascript dedicated web worker to send messages on demand - javascript

Is it possible to write a simple dedicated web worker so it process something continuously and sends its state only when the client asks.
What I've done so far, the Client file :
<script>
// spawn a worker
var worker = new Worker('basic-worker.js');
// decide what to do when the worker sends us a message
worker.onmessage = function(e){
document.getElementById('result').textContent = e.data;
};
</script>
<html>
<head></head>
<body>
<p>The Highest prime number discovered so far : <outpout id="result"></output></p>
</body>
</html>
The worker file :
var n = 0;
search: while (true) {
n += 1;
for (var i = 2; i <= Math.sqrt(n); i += 1)
if (n % i == 0)
continue search;
// found a prime !
postMessage(n);
}
As you can see the worker send continuously the primes it founds. I would like to be able to launch the prime calculation and asks the worker to send the latest prime he founds when I click a button on the client for example. That would be something like (I know it cannot work as but to give a general idea of what i want) :
Worker file :
var n = 0;
var lPrime = 0;
// post last prime number when receiving a message
onmessage = function(e) {
postMessage(lPrime);
}
// continously search for prime numbers
search: while (true) {
n += 1;
for (var i = 2; i <= Math.sqrt(n); i += 1)
if (n % i == 0)
continue search;
// found a prime !
//postMessage(n);
lPrime = n;
}
Client file :
<script>
// spawn a worker
var worker = new Worker('basic-worker.js');
// what to do when the worker sends us a message
worker.onmessage = function(e){
document.getElementById('result').textContent = e.data;
};
// post to the worker so the worker sends us the latest prime found
function askPrime(){
worker.postMessage();
};
</script>
<html>
<head></head>
<body>
<p>The Highest prime number discovered so far : <outpout id="result"></output></p>
<input type="button" onclick="askPrime();">
</body>
</html>

This is not a good pattern. Workers are single-threaded, so at any given moment, they can either:
perform a calculation,
send an event, or
respond to an event.
While your worker is calculating, it cannot respond to events. When you send a request for the latest prime, you must wait for the worker to finish what it is doing before it can process your request. Of course, you can use setTimeout (or other methods) to allow the browser to "interrupt" the current work (see Javascript - how to avoid blocking the browser while doing heavy work?), but the entire point of workers is to save you from resorting to such needless complexity.
A better pattern would be a non-worker variable that holds the latest prime, and is updated by the worker whenever it find a new prime. You can query that variable whenever you need the latest prime, and you never need to wait for the worker to process your request.
// spawn a worker
var worker = new Worker('basic-worker.js');
// store the latest prime as it is produced
var latestPrime = 1;
worker.onmessage = function(e){
latestPrime = e.data;
};
// fetch the latest prime from the variable
function askPrime(){
document.getElementById('result').textContent = latestPrime;
};
You could also accomplish this pattern by having two workers:
The main script spawns a worker that holds the latest known prime.
That worker spawns a second worker that actually does the work and reports new primes to the first worker as they are discovered.
That way, the first worker doesn't do any work, and is always free to respond with the latest prime, and the second worker never needs to stop its computations to respond to requests.

Related

How to terminate a Web worker inside addEventListener()?

I have a Web Worker here.
//worker.js
self.addEventListener("message", function(event) {
self.postMessage(event.data);
self.close();
});
And here main js file.
//main.js
var webworker = new Worker("worker.js");
webworker.postMessage('Send something.');
webworker.addEventListener("message", function(event) {
webworker.terminate();
});
The problem is that the "webworker.terminate();" inside the addEventListener does n't seem to work.There were no warnings or errors, but the web worker was not terminated.
I am creating an autocomplete input, it creates workers every typing, i use workers to search through a large data for the results, the problem that too many workers were created that i want to terminate them. i know they are still running because of the cpu consumed percentage and the code after "webworker.terminate();" still proceed while they should not.
I simplize the code here, i create 8 workers for each typings in the input field, they work with the input value every types.The problem is that the old workers are still active while i type new value into the input field, i just want to terminate them.
inp.addEventListener("input", function(e) {
for (let i = 0; i < 8; i++) {
var webworker = new Worker("autocomplete/worker.js");
webworker.postMessage(inp.value);
webworker.addEventListener("message", function(event) {
//do thing
})
}
}
Is there a way to terminate the Web worker in this scenario? I just want to terminate the Worker right after the response's arrival.
Thank you and sorry for my bad English.

implementing timer in javascript using web workers

I am trying to implement a timer in javascript using web workers. I wrote the following code. But it's not working. Can someone point out why it's not working? I don't have much experience with javascript. So, it would be great if someone explains the reason in great detail.
Here's what I'm trying to do:
First creating a sharedArrayBuffer and a worker thread. And creating another array, on which, I will do some work and want to count the time. Then sending the sharedArrayBuffer to worker thread which increments the first value in the array in a for loop. Finally, I am reading that value in the main.js and I'm getting 0 every time.
main.js
var buffer = new SharedArrayBuffer(1024);
var i;
var uint32 = new Uint32Array(buffer);
var myWorker = new Worker('worker.js');
var array = new Uint32Array(8);
array[0] = 0;
console.log(Atomics.load(uint32,0),array[0]);
myWorker.postMessage(buffer);
for(i=0;i<300000000;i++) {
array[0] += i;
}
console.log(i,Atomics.load(uint32,0),array[0]);
worker.js
onmessage = function(buffer) {
console.log('from worker');
var uint32 = new Uint32Array(buffer.data);
for(i=0; ;i++) {
uint32[0] += 1;
};
}
You should not be using code like this to try and determine how long code takes to run. It's non-sensical because incrementing the count in an array is not tied to time or any unit of measurement. Instead, there are APIs which can be used to evaluate performance, such as console.time():
onmessage = function(buffer) {
console.time('TimeSpentInWorker');
// Your code...
console.timeEnd('TimeSpentInWorker');
};
You could also compare the difference between calling Date.now() twice or look into the Performance API.

node js clustering is repeating the same task on all 8 processes

I've been trying to enable clustering in my node js app. Currently I use this snippet to enable it:
var cluster = require('cluster');
if (cluster.isMaster) {
// Count the machine's CPUs
var cpuCount = require('os').cpus().length;
// Create a worker for each CPU
for (var i = 0; i < cpuCount; i += 1) {
cluster.fork();
}
// Listen for dying workers
cluster.on('exit', function () {
cluster.fork();
});
}
And basically my code performs writes to a Firebase database based on conditions. The problem is that the writes are occurring 8 times each, rather than one worker just taking care of one write task, it seems that all threads are performing all tasks. Is there a way to avoid this? If so, can someone point me in the direction of some resources on this? I can't find anything on google for using Firebase with node js clustering. Here is an example of the way one of my functions work (ref is my firebase reference):
ref.child('user-sent').on('child_added', function(snapshot) {
var message = snapshot.child('message');
payload['user-received/'] = message;
ref.update(payload); // this occurs once for each fork so it updates 8 times
});
If you're spawning 8 threads and each thread attaches a listener on the same location (user-sent), then each thread will fire the child_added event for each child under that location. This is the expected behavior.
If you want to implement a worker queue, where each node under user-sent is only handled by one thread, you'll have to use a work-distribution mechanism that ensures only one thread can claim each node.
The firebase-queue library implements such a work claim mechanism, using Firebase Database transactions. It's been used to scale to a small to medium number of workers (think < 10, not dozens).

The JavaScript Event Loop and Web Workers

So I've been having a long talk with a colleague regarding the JavaScript event loop and the use of Web Workers. In a single Web page, different Web Workers have different stacks, heaps, and message queues, form here, specifically:
A web worker or a cross-origin iframe has its own stack, heap, and message
queue. Two distinct runtimes can only communicate through sending messages via
the postMessage method. This method adds a message to the other runtime if the
latter listens to message events.
but are all the messages executed inside the same event loop, or does each Web Worker have its own event loop?
I'm asking this because I have two Web Workers in a page, one executes a very computationally-heavy operation in sequence, while the other just handles a WebRTC connection.
I will not go into details but it seems to me that the computationally-heavy Web Worker is taking away so much computational time out of the JavaScript event loop that the other Worker, that only has to keep the connection alive (through heartbeat I suppose) isn't able to do so, and the connection is eventually lost.
This is what I believe. If that is not the case, and the two Web Workers work on different event loops then I cannot explain why the connection is lost when the load on the computing Web Worker is heavy (when the load is light then the connection is not lost).
Each worker has its own event loop. From the specification:
Each WorkerGlobalScope object has a distinct event loop, separate from those used by units of related similar-origin browsing contexts.
and then here:
The global scope is the "inside" of a worker.
...which is followed by the definition of the WorkerGlobalScope interface referenced in the earlier quote.
Your computation-heavy worker might be dominating the available processing time, but it isn't blocking the other worker's event loop.
We can also readily check this with a quick test:
page.html:
<!DOCTYPE HTML "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta charset="UTF-8">
<title>Two Workers</title>
<style>
body {
font-family: sans-serif;
}
pre {
margin: 0;
padding: 0;
}
</style>
</head>
<body>
<div>Fast: <span id="fast"></span></div>
<div>Slow: <span id="slow"></span></div>
<script>
(function() {
var fastWorker = new Worker("fastworker.js");
var fast = document.getElementById("fast");
var slowWorker = new Worker("slowworker.js");
var slow = document.getElementById("slow");
fastWorker.addEventListener("message", function(e) {
fast.innerHTML = e.data || "??";
fastWorker.postMessage("ping");
});
slowWorker.addEventListener("message", function(e) {
slow.innerHTML = e.data || "??";
slowWorker.postMessage("ping");
});
fastWorker.postMessage("start");
slowWorker.postMessage("start");
})();
</script>
</body>
</html>
slowworker.js:
var counter = 0;
self.addEventListener("message", function(e) {
var done = Date.now() + 1000; // 1 second
while (Date.now() < done) {
// Busy wait (boo!)
}
++counter;
self.postMessage(counter);
});
fastworker.js:
var counter = 0;
self.addEventListener("message", function(e) {
var done = Date.now() + 100; // 100ms
while (Date.now() < done) {
// Busy wait (boo!)
}
++counter;
self.postMessage(counter);
});
As you can see, "fast"'s number goes up much more quickly than "slow", showing it's processing its messages.
(I could have made one worker file and sent the delay in the start command, but...)

Is it possible to terminate a running web worker?

I have a web worker running a time-consuming routine task with ajax-requests. Can I terminate them from a main thread not waiting for them to finish?
That's how I spawn and terminate it:
$("button.parse-categories").click(function() {
if (parseCategoriesActive==false) {
parseCategoriesActive = true;
parseCategoriesWorker = new Worker("parseCategories.js");
$("button.parse-categories-cancel").click(function() {
parseCategoriesWorker.terminate();
parseCategoriesActive = false;
});
}
});
This is the worker code:
function myAjax(url, async, callback) {
xmlhttp = new XMLHttpRequest();
xmlhttp.onreadystatechange=function() {
if (xmlhttp.readyState==4 && xmlhttp.status==200)
callback(xmlhttp.responseText);
if (xmlhttp.readyState==4 && xmlhttp.status!=200) {
self.postMessage("error");
throw "error in ajax: "+xmlhttp.status;
}
}
xmlhttp.open("GET", url, async);
xmlhttp.send();
}
var parseCategoriesActive = true;
var counter = 0;
do {
myAjax('parser.php', false, function(resp) {
if (resp=='success')
parseCategoriesActive = false;
else {
counter += Number(resp);
self.postMessage(counter);
}
});
} while (parseCategoriesActive==true);
You can kill any webworker using terminate().
Citing from MDN:
The Worker.terminate() method immediately terminates the Worker. This does not offer the worker an opportunity to finish its operations; it is simply stopped at once.
I did this simple script and it seems the problem with FF and Chrome is still here:
var myWorker = new Worker("w.js");
myWorker.onmessage = function(e) {
console.log('Message received from worker:'+e.data);
}
myWorker.postMessage(100);
setTimeout(stopWW, 500) ;
function stopWW(){
console.log('TERMINATE');
myWorker.terminate();
}
while the webworker is:
onmessage = function(e) {
var n = e.data;
console.log('Message received from main script:'+n);
for (var i=0;i<n;i++){
console.log('i='+i);
postMessage(i);
}
}
as soon as the main thread terminates the webworker so it does not receive postmessage any more BUT the webworker is still running under the scenes, this is an output:
"Message received from main script:100" w.js:3:2
"i=0" w.js:5:1
"Message received from worker:0" w.html:6:2
"i=1" w.js:5:1
...
"Message received from worker:21" w.html:6:2
"i=22" w.js:5:1
"TERMINATE" w.html:13:1
"i=23" w.js:5:1
"i=24" w.js:5:1
...
"i=99" w.js:5:1
The OP clearly asked:
Can I terminate them from a main thread not waiting for them to
finish?
He is not asking how a worker should gracefully exit internally. He wants to very ungracefully kill any worker from an external thread.
You can kill any web worker by calling its terminate() function, assuming the worker instance is in context scope.
Web workers also have a life-cycle. They will self-terminate under specific conditions.
You can kill a dedicated worker by closing the page/frame that created it by calling window.close() from the main thread.
That is what dedicated means - it is only allowed to serve a single
page. Closing the page invokes the dedicated worker's
self-termination sequence.
You can kill a shared worker by closing all the pages of the domain
that created it. That is what shared means - it is
allowed to serve multiple pages from a domain. Closing all domain pages
invokes the shared worker's self-termination sequence.
Other worker types can also be killed by closing all domain pages.
Here is an example of terminating some unwelcome worker bots that steal your CPU:
// Create worker bots that consume CPU threads
var Blob = window.Blob;
var URL = window.webkitURL || window.URL;
var numbots = 4;
var bots = null;
var bot = "while(true){}"; // Nasty little botses!
bot = new Blob([bot], { type: 'text/javascript' });
bot = URL.createObjectURL(bot);
function terminator(){
if (bots!=null){
// Hasta la vista ...
for (worker in bots)
bots[worker].terminate();
bots = null;
document.getElementById("terminator").play();
document.getElementById("button").src = "https://dcerisano.github.io/assets/img/launch.jpg"
}
else{
// Launch Intruders ...
bots = [];
for (var i=0; i<numbots; i++)
bots[i] = new Worker(bot);
document.getElementById("alert").play();
document.getElementById("button").src = "https://dcerisano.github.io/assets/img/terminate.jpg"
}
}
<img id="button" src="https://dcerisano.github.io/assets/img/launch.jpg" width="100%" onclick="terminator()">
<audio id="terminator" src="https://dcerisano.github.io/assets/audio/hastalavista.mp3">
<audio id="alert" src="https://dcerisano.github.io/assets/audio/alert.mp3">
Use your CPU monitor to confirm snippet is running.
Press button to terminate worker bots.
"Hide results" will close the snippet page/frame, also terminating
worker bots.
Hopefully this answer will be useful to those wishing to build apps that hunt and kill unwelcome bots implemented as web workers (eg. bitmining bots that steal your electricity, bandwidth, and make you an accessory to Ponzi)
For example, this Chrome extension detects and selectively blocks web workers (all sorts of bots, including miners).

Categories

Resources