2 min read

Introducing worker-to-promise

Introducing worker-to-promise

From time to time, I make a thing and I like it.

Recently, I created a library that is quite unoriginally named Worker-to-Promise, which allows a developer to call methods on a Web Worker and retrieve the result in the form of a promise.

The usage of this is fairly simple, and can be seen on the demo page. The demo will perform a simple multiplication of two numbers in a worker.

I designed w2p with the concept of registering tasks to perform in the worker so that you don't have to manually rig everything together in your this.onmessage call in your worker.

// We can setup a single default onmessage that will occur if no tasks have been registered
this.onmessage = function (e) {
    console.log('Message received from main script that did not match any tasks');
    console.log(e);
    var workerResult = 'Result: ' + (e.data.payload[0] * e.data.payload[1]);
    console.log('Posting message back to main script');
    this.postMessage(workerResult);
};

self.importScripts('/js/w2p/worker-to-promise-dedicated-worker.js');
// Now we can register our tasks

this.registerTask("multiply", function (data, success, err) {
    success(data[0] * data[1]);
});

The line self.importScripts('/js/w2p/worker-to-promise-dedicated-worker.js'); will override the this.onmessage method, but is created with a fallbcak in mind. The original onmessage is stored, and if the received message is not in the format of a w2p envelop then it will be routed to the original this.onmessage method; this is why, in the code above, we this.onmessage before importing w2p.

You can register tasks in your worker that will take in a single parameter and will utilize a success / err callback to determine what result type to send back to the caller, allowing for asynchronous code to occur; errors are also caught and sent back as a rejected promise to the caller.

The caller code attempts to emulate the Dedicated Worker spec, with the exception of an added parameter at the end of the postMessage method to set the task to run:

(function () {
    var first = document.getElementById('number1');
    var second = document.getElementById('number2');
    var resultField = document.getElementById("result-number");

    if (window.Worker) {
        // Check if Browser supports the Worker api.
        // Requries script name as input
        var myWorker = new w2p.Worker("js/worker.js");

        // onkeyup could be used instead of onchange if you wanted to update the answer every time
        // an entered value is changed, and you don't want to have to unfocus the field to update its .value

        first.onchange = function () {
            myWorker.postMessage([parseInt(first.value), parseInt(second.value)], "multiply").then(function (result) {
                resultField.textContent = document.createTextNode(result).textContent;
                console.log(result);
            })
        };

        second.onchange = function () {
            myWorker.postMessage([parseInt(first.value), parseInt(second.value)], "multiply").then(function (result) {
                resultField.textContent = document.createTextNode(result).textContent;
                console.log(result);
            });
        };
    }
})();

The major difference is that you may then use the result of your postMessage as a promise.