9

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);
Gherman
  • 6,768
  • 10
  • 48
  • 75
  • that depends on how your spawning the thread. You need to show your code or at least give us more info. – Liam Feb 07 '14 at 10:37
  • 1
    I've already asked several questions on web workers and encountered a lot of bugs and malfunctioning. This feature seems nice yet very raw. I don't want to try to use it anymore in the nearest future. – Gherman Feb 07 '14 at 11:37
  • Worth noting it does work allright in Opera 12 \m/. At least it seems so. – Gherman Feb 07 '14 at 13:51
  • @German I think your logic is incorrect here. From a first glance you create AJAX-requests until one of them succeeds. But in the meanwhile plenty of other AJAX requests have been created and their respective callbacks wait for their completion. I think it might be better to start a new request, only when the last request has failed. Thus you have only one active AJAX at any time. – Sirko Feb 07 '14 at 14:25
  • Oh, sorry. That's not the actual code. I spoiled it when copied. The flag was `false` and they were sunchronous, of course. – Gherman Feb 07 '14 at 16:54

3 Answers3

16

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.

Sirko
  • 72,589
  • 19
  • 149
  • 183
  • 4
    "You are `terminate()`ed" - [Thanks Arnold](http://www.youtube.com/watch?v=iy_BBBGBpqA) – Wez Feb 07 '14 at 10:41
  • 3
    That's what I tried to do. But the worker ignored this method completely. I'm sure it gets run but does nothing because other commands after `terminate()` are executed OK. That's why I thought maybe it's because the worker havn't stoped or because it is waiting for an ajax request to finish. It's interesting to mention that firefox process stays after closing firefox if workers don't finish as a zombie. It can't be run another time until you kill the process manually. (I'm on Ubuntu) – Gherman Feb 07 '14 at 10:45
  • @German It seems related to this bugreport: https://bugzilla.mozilla.org/show_bug.cgi?id=872526 – Sirko Feb 07 '14 at 10:51
  • `terminate()` also doesn't work in Chrome though Chrome doesn't have this bug when closing browser. – Gherman Feb 07 '14 at 11:05
  • @German Interesting. Could you put up an example somehow? I guess pending AJAX callbacks won't be cleared, so maybe some refactoring could at least work around the problem. – Sirko Feb 07 '14 at 11:12
3

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
mixer
  • 59
  • 5
  • the best I did is to use **myWorker.postMessage({type:'loop',n:100});** to make it executes a job and **myWorker.postMessage({type:'stop',n:0});** to make the WW to kill itself via _self.close()_. Furthermore you need to change the WW _postmessage_ in order to interrupt itself for a while (eg. _setTimeout(myJob, 50)_). It seems the WW is able to receive another postmessage (from the main thread) only when in sleeping mode. – mixer Sep 04 '15 at 21:01
2

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).

Dominic Cerisano
  • 3,522
  • 1
  • 31
  • 44
  • Since I included a working example, you can terminate by simply closing the page/frame which is called on the main thread. This invokes worker self-termination. Call `window.close()` on the main thread. – Dominic Cerisano Jul 29 '22 at 15:53