How to post a message from a web worker to another web worker Javascript

1.3k Views Asked by At

I am trying to send a message from one worker to the other (and then respond back through a SharedArrayBuffer). But I can't find any information on posting a message to another worker from within a worker. How would I go about doing it? (I tried posting the worker itself as a message, but it doesn't work).

Here is my code:

index.html:

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
</head>

<body>
    <script type='text/javascript'>
        var s_curFile;

        var s_curFile2;

        var s_SAB = new SharedArrayBuffer(65563);

        var u8buf = new Uint8Array(s_SAB);

        function JSprocessFilePicker( input )
        {
            let url = input.value;
            let ext = url.substring( url.lastIndexOf( '.' ) + 1 ).toLowerCase();
            if ( input.files && input.files[0] && ( ext == "txt" ) )
            {
                s_curFile = input.files[0];

                try {
                    fileWorker.postMessage({
                        'cmd' : 'file',
                        'file' : s_curFile
                    });
                } catch(e) {
                    alert('Can\'t spawn files to worker - '+e)
                }

                try {
                    worker.postMessage({
                        'cmd' : 'run'
                    });
                } catch(e) {
                    alert('Can\'t spawn files to worker - '+e)
                }

            }
        }
        u8buf[0] = 0;

        var worker = new Worker('worker.js');
        var fileWorker = new Worker('fileWorker.js');

        try {
            worker.postMessage({
                'cmd' : 'SAB',
                'sab' : s_SAB
            }, fileWorker);
        } catch(e) {
            alert('Can\'t send Shared Array Buffer to worker - '+e);
        }

        try {
            fileWorker.postMessage({
                'cmd' : 'SAB',
                'sab' : s_SAB
            });
        } catch(e) {
            alert('Can\'t send Shared Array Buffer to worker - '+e);
        }


        var input = document.createElement( "input" );
        input.setAttribute( "id", "file_picker" );
        input.setAttribute( "type", "file" );
        input.setAttribute( "accept", ".fzp" );
        input.setAttribute( "onchange", "JSprocessFilePicker(this)" );
    </script>


    <button onclick="input.click();">Open</button>

</body>

</html>

worker.js:

var s_SAB;
var u8buf;
var fileWorker;

self.onmessage = function(e) {
    if( e.data.cmd === 'SAB' )
    {
        s_SAB = e.data.sab;
        u8buf = new Uint8Array(s_SAB);
        fileWorker = e.data.fileWorker;
        console.log(s_SAB);
        console.log(fileWorker);
    }
    else if( e.data.cmd === 'run' )
    {
        try {
            fileWorker.postMessage({
                'cmd' : 'load'
            });
        } catch(e) {
            console.log('Can\'t post message from web worker - '+e); //I GET AN ERROR HERE
        }
    
        while(u8buf[0] === 0)
        {
            //This loop here I am doing heavy processing, that i cannot change really
            console.log("worker waiting for file...\n");
        }
        console.log("worker got data!\n");
        let Idx;
        for (Idx = 0; Idx < 20; ++Idx) {
            console.log(u8buf[Idx+1]);
        }
    }

fileWorker.js

var s_curFile;

var s_SAB;
var u8buf;

let xhrReq = new XMLHttpRequest();
xhrReq.overrideMimeType('text/plain; charset=x-user-defined');

self.onmessage = function(e) {
    if( e.data.cmd === 'file' )
    {
        s_curFile = e.data.file;
    
        console.log("got file!");
    }
    else if( e.data.cmd === 'SAB' )
    {
        s_SAB = e.data.sab;
        u8buf = new Uint8Array(s_SAB);
        console.log(s_SAB);

    }
    else if( e.data.cmd === 'load' )
    {

        let uri = URL.createObjectURL(s_curFile.slice(40, 100));
        xhrReq.open('GET', uri, true);
        xhrReq.send(null);
        URL.revokeObjectURL(uri);
        let Idx;
        let sz = xhrReq.response.length;
        console.log("Response Size: "+sz);
        for (Idx = 0; Idx < sz; ++Idx) {
            u8buf[Idx+1] = xhrReq.response.charCodeAt(Idx);
        }
        u8buf[0] = 1;
    }
}
1

There are 1 best solutions below

1
On

TL;DR Do you really want to do this?

In order to even have access to a SharedArrayBuffer you have to set some specific headers to meet the post-Spectre security requirements. You can use Atomics.wait* to block a worker thread on change to a shared buffer to communicate from one worker to another if you really want...

Alternatively:

You could simply forward messages from one worker to the main thread using a simple postMessage to other workers, a very basic pub-sub if you will. Much easier to pull off.

* Only works with an Int32Array as the underlying buffer, not Uint8Array like you have in your example.