Vũ Nguyễn
Vũ Nguyễn

Reputation: 151

Web workers to make setInterval work as normal

I'm trying to create an automated program by using javascript console only. I need to use setInterval to make a loop for my program. The relevant part of the code is just like this:

refreshIntervalId=setInterval(tick,500);

So the "tick" function will be recalled every 500 milliseconds.

But the problem is whenever the browser tab is being inactive i.e I minimize it to do something else, the setInterval stop working. When I open the browser again it suddenly repeated so many times to "compensate" for the time the fucntion had not been executed. It is not what I am expected.

I've heard that web workers can solve the problem. I just need one (or two) single code line to work via web workers but since I'm an amateur to IT, I cannot understand all of them. I can only access the browser via JS console and I don't have the access to both HTML and its scripts.

My main goal is to make it loop and break it (setInterval and clearInterval) normally while the browser can still in the inactive mode.

What should I do now? Thank you for reading and hopefully someone can help me solve it!

Upvotes: 9

Views: 12688

Answers (2)

CttCJim
CttCJim

Reputation: 106

Excellent basic solutions above. If you wanna get more complex, this is part of my current WIP implementation (for some reason intervals only fire once, but timeouts are working great). This is inside the worker.js file.

onmessage = (e) => {
    //e.data is the data sent
    var data;
    if(typeof(e.data)=="string") {
        data = {
            command:e.data
        };
    } else {
        data = e.data;
    }
    if(typeof(data.command)=="undefined") {
        sendError("Invalid data received by workerTimeouts.js (no command)",e.data);
        return;
    }

    switch(data.command.toLowerCase()) {
        case "dump": {
            postMessage({
                clocks:clocks,
                command:'dump'
            });
        }
        case "start": {
            if(typeof(data.name)=="undefined") {
                sendError("Invalid data received by workerTimeouts.js (name)",e.data);
                break;
            }
            if(typeof(data.duration)=="undefined") {
                sendError("Invalid data received by workerTimeouts.js (duration)",e.data);
                break;
            }
            if(typeof(data.type)=="undefined") {
                data.type = "timeout";
            }
            if(typeof(data.payload)=="undefined") {
                data.payload = null;
            }
            //start the timeout
            if (data.type.toLowerCase() == "timeout") {
                startTimeout(data.name,data.duration,data.payload);
            }
            if (data.type.toLowerCase() == "interval") {
                startInterval(data.name,data.duration,data.payload);
            }
        } break;
        default: {
            sendError("Invalid command received by workerTimeouts.js (command)",e.data);
        }
    }
}

back in the main thread i do startTimer("keepalive",60000,keepalive,"timeout");

function startTimer(name,duration,callback,type="timeout",payload=null) {
    if(window.workerSupported) {
        //create callback
        window.timerCallbacks[name] = callback;
        //start timer
        window.workersBin.timers.postMessage({
            command:'start',
            name:name,
            duration:duration,
            type:type,
            payload:payload
        });
    } else {
        if(type.toLocaleUpperCase()=="timeout") {
            setTimeout(()=>{callback(payload)},duration);
        }
        if(type.toLocaleUpperCase()=="interval") {
            setInterval(()=>{callback(payload)},duration);
        }
    }
}

there's some things missing here, like on page load i determine if the worker is supported or not so legacy browsers will still sort of work for my site (using setTimeout instead of the worker).

It sucks that intervals arent working for me as i have a background loop that i want on a 250ms interval, but i solve that by using a timer and then having bgLoop() end by starting a new timer for bgLoop(), which i prefer anyway as it prevents race conditions due to execution lag, especially if i'm doing Ajax in the loop.

Upvotes: 0

DavidsKanal
DavidsKanal

Reputation: 839

Okay, this might be a little late, but I hope this will be of some use anyway.

Put this code into your main JavaScript:

let intervalWorker = new Worker('worker.js');
intervalWorker.onmessage = /* Your function here */;

...and this could be an example for the code in a new file 'worker.js':

setInterval(() => {
    postMessage();
}, 500);

The worker will post an empty message to your main thread every 500ms. When that happens, your main thread will call the "onmessage" function on your worker. So just assign whatever you function you need to happen every 500ms to that property.

Note: You need to run this on a server for the JS to load the worker file - otherwise it will complain because of 'safety' reasons.

Upvotes: 11

Related Questions