4
votes

My intention is for one message to be passed to the worker after it is created, and for that message to be logged by the worker and a reply to be sent back. What happens is that the message sent to the web worker is logged twice, and only one reply is sent. If you refresh the page or pause execution using developer tools, the message is correctly logged once.

I created a miniature extension to demonstrate this. There is a background script listening for a click on the browser action button. When it is clicked, a new tab is opened, and this tab generates 2 web workers. These workers listen for messages and log them, and send a reply stating that the message was recieved. The newly opened tab then sends the message, and logs the reply.

Google drive with all of the files needed to run this as a chrome extension

Image demonstrating the double logging of the message

Some references:

I have been working on my extension for a while now and have run into all sorts of fun bugs, particularly with async code. In the end I was always able to either debug them myself or find a reference that explained the problem. Thank you for any help you can give!

background.js

const tabUrl = chrome.extension.getURL("tab.html");

function browserActionCallback(tab) {

    function createResultsTab() {

        chrome.tabs.create({
            "url": tabUrl,
            "index": tab.index + 1
        }, function() {
            console.log("Tab created");         
        });
    }

    (function tabHandler() {
        chrome.tabs.query({"url":tabUrl}, (tabQueryResults) => {
            if (tabQueryResults.length > 0) {
                chrome.tabs.remove(tabQueryResults[0].id, () => {
                    createResultsTab();
                });
            } else {
                createResultsTab();
            }
        });
    })();
}

chrome.browserAction.onClicked.addListener((tab) => {
    browserActionCallback(tab);
});

tab.js

function createWorkers(num) {
    /*in original extension I intended to let the user change number of workers in options*/

    var workers = new Array(num);
    for (let i = 0; i < num; i++) {
        workers[i] = new Worker("worker.js");
    }
    return workers;
}

function messageWorkers(workers) {
    let len = workers.length
    for (let i = 0; i < len; i++) {
        let message = "Connection " + i + " started";
        workers[i].postMessage(message);
        workers[i].onmessage = function(e) {
            console.log(e.data);            
        }
    }   
}

var numWorkers = 2;
const WORKERS = createWorkers(numWorkers);
console.log("Before");
messageWorkers(WORKERS);
console.log("After");

worker.js

onmessage = function(msg) {
    console.log(msg.data);
    postMessage("Worker reply- " + msg.data);
}

EDIT 1: changing the tab.js messageWorkers function's for loop such that onmessage is set before postMessage does not change the results. Sending multiple messages also doesn't change results. If I have a console.log statement at the start of worker's onmessage function which logs that the function has begun, it too logs twice. To reiterate, this only happens when this tab is created by the background script and not when the page is refreshed or debugger is used.

EDIT 2: in the devtools console, there is a drop down menu to choose between top and workers. Checking the option 'selected context only' makes the repeated logging disappear, however this view is a little narrower than I would like

1

1 Answers

2
votes

I can confirm this issue. Sample code to reproduce it:

index.js

function worker() {
  console.log('this logged twice');
}

const workerBlob = new Blob(
  [worker.toString().match(/function[^{]+\{([\s\S]*)\}$/)[1]],
  {type: 'text/javascript'}
);
const workerBlobUrl = URL.createObjectURL(workerBlob);

new Worker(workerBlobUrl);

Use index.js as a Chrome extension background script and "this logged twice" will be logged twice when loading the extension or re-launching Chrome.