DEV Community

Cover image for ClockWorks, Web Worker library
Reece M
Reece M

Posted on

ClockWorks, Web Worker library

As a spin off from another simple static web page app that I made the other day using Alpine.js and TailwindCSS I was in the need of pushing the setInterval() functions off to the Web Worker API.

The result is a naïve implementation of a stack system to manage a set if intervals and timeouts (setTimeout() isn't available as of v0.1.x).

The whole package is cool in that it is currently less than 1KB zipped as of version 0.1, which is quite neat.

The first implementation of the system was baked into the app, and gave me a place to test the base logic and ideas.

After an afternoon to stick the package together, the simple way to have a interval firing on the web worker is below:

IMPORTANT Please note that the first few versions are only pushing the timers into the web worker, the callback is still done in the main thread.

Details

The idea was simple, and only after I added some tags to the repo did I find it's not the first but glad to know that I was on the right track before learning how others had done similar :D.

Usage is pretty simple, you can spin up the intervals/timers when you instantiate the class like so:

/**
 * Create a new instance of the class and then print the time to the console.
 *
 * We will also remove the clock after 5 seconds, by counting a variable.
 */
let triggers = 0;
let clockWorks = new ClockWorks([
    {
        name: 'clock',
        time: 1000,
        callback: () => {
            console.log((new Date()).toLocaleString())
        }
    },
    {
        name: 'remove-clock',
        time: 1000,
        callback: () => {
            if (++triggers >= 5) {
                $clock.pull('clock')
                $clock.pull('remove-clock')
            }
        }
    }
])
Enter fullscreen mode Exit fullscreen mode

Or during the process of running your app you can call the push or pull methods, these allow you to selectively add or remove a timer from the stack by using the name you defined in the object.

Example of adding one:

clockWorks.push({
    name: 'new-timer',
    time: 5000,
    callback: () => {
        console.log('New interval has fired at [%s]', (new Date()).toLocaleString());
    }
})
Enter fullscreen mode Exit fullscreen mode

And to pull that same one later on would be:

clockWorks.pull('new-timer');
Enter fullscreen mode Exit fullscreen mode

Web Worker

I got around having to send a separate web worker as the implementation shouldn't need to be edited by bundling in the package and then using the Blob technique to get it running.

This actually has a neat side effect in that because each web worker gets a new blob url, you can create multiple instances with their own worker each.

The code that makes it possible is from a bit of finding it isn't the first time people have tried this and also the classic SO questions that have been asked. From looking at a bunch, I decided that the best way would be to do the following when installing a new Web Worker:

import workerFile from './worker';

// ... later on

// in the startWorker method, we use blob to make the url
// we convert the function into a string, and then wrap it in IIFE thingy so it runs and works correctly, that was a bit of fun.
var blob = new Blob([`(${workerFile.toString()})();`], { type: 'text/javascript' });

// then put it in a Worker thingy :)
this.worker = new Worker(window.URL.createObjectURL(blob));
Enter fullscreen mode Exit fullscreen mode

The actual worker file is rather bland, but you can check it out here: worker.js

This works pretty well as its able to have the script bundled and then add it dynamically.

Microbundle

For this package I decided to use microbundle as I was not wanting a whole complex build system for something that was going to be slap stick.

It worked as advertised 🎉, LOL.

But it did to as it was labeled, straight forward and easy to get running bundler. Which is nice for this build as I was able to target different things.

Planned features

A planned feature that I will implement is to be able to define just a straight forward callback, with an optional name. This would then be tracked internally and the user will get an id they can use to pull it from the stack of timers if it is an interval one.

This would give the user the ability to do this:


let id = clockWorks.push(() => {
    var element = document.querySelector('[worker-main]');
    // select Alpine Element
    element.__x.refreshGraphData();
})
Enter fullscreen mode Exit fullscreen mode

GitHub repo.

If you would like to see more details or how to make use of it you can check out the Git repo.

The library is currently in pre-release as there are still some features that can be added. PRs for any bugs are welcome too.

GitHub logo ReeceM / clockworks

Webworker for managing timers and intervals

0_o

Top comments (1)

Collapse
 
reecem profile image
Reece M

Sorry if you have seen this again, I hit publish too soon 🤦‍♂️