I am working on a program that relies heavily on web workers. But I am unable to get them to function as required. This is just a minified example but the format is more or less similar.
I have 4 files:
index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello World!</title>
</head>
<body>
<button id="btn">GO</button>
<script src="./index.js" type="module"></script>
</body>
</html>
index.js
const worker = new Worker("./worker.js", {type: "module"})
worker.addEventListener("message", async ({ data }) => {
console.log(data);
});
worker.onerror = function (event) {
throw new Error(event.message + " (" + event.filename + ":" + event.lineno + ")");
}
window.sendMessage = function (args) {
worker.postMessage(args);
};
async function onclick() {
sendMessage("World")
}
document.getElementById("btn").addEventListener("click", onclick, false);
worker.js
import * as p from "hello.js"
console.log("Worker Started")
onmessage = function (e) {
p.greet(e.data)
}
hello.js
export function greet(who){
alert("Hello " + who)
}
The worker should output "Worker Started".
When the button is clicked, the UI thread will send a message to the worker which will then call the greet function.
However the worker immediately errors and returns a value via worker.onerror
The value it returns in Chromium(and Chromium based) is undefined
Uncaught Error: undefined (undefined:undefined)
at Worker.worker.onerror (index.js:8:11)
I have tested this across various browsers on 2 computers with 3 different operating systems with no success.
From what I understand. Firefox does not support this the way I am doing it.
Uncaught Error: SyntaxError: import declarations may only appear at top level of a module (http://localhost:8000/worker.js:1)
I have taken a look at this answer here
https://stackoverflow.com/a/33432215/19140286
however It does not let me import a file into my worker function.
Running chrome with
chromium --allow-file-access-from-files
does not solve the issue
examples are run with a local server via
python -m http.server
Here is a repository with the files
https://github.com/KivalM/web-workers
Such an error on a Worker without a .message, .filename, or .lineno points toward a network error, if it was a script error you'd get these filled.
Maybe your worker URL is also invalid, but at least your module one is.
If you don't use module maps, you must prepend ./ to relative import URLs.
import * as p from "./hello.js"
Once this is fixed, you'll face a new error, a script error this time:
alert() is only accessible in Window contexts. You can't call this method from a Worker. You should anyway avoid its use even in Window contexts because its influence on the JS environment is far from being obvious and usually isn't as useful in debugging as the Console API.
Related
I'm starting with Node.js and I have already a problem in my first program. Below is the code I'm using. Index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Random Temperatures</title>
</head>
<body>
<input type="text" id="tb" name="tb" />
<input type="button" value="Random Number!" id="myButton" name="myButton"/>
<script src="client.js"></script>
</body>
</html>
Client.js:
const textBox = document.getElementById('tb');
const button = document.getElementById('myButton');
button.addEventListener('click', function(e) {
var rnd = Math.floor(Math.random() * 100);
textBox.value = rnd;
});
Server.js:
var app = require('http').createServer(response);
var fs = require('fs');
app.listen(8080);
console.log("App running…");
function response(req, res) {
fs.readFile(__dirname + '/public/index.html',
function (err, data) {
if (err) {
res.writeHead(500);
return res.end('Failed to load file index.html');
}
res.writeHead(200);
res.end(data);
});
}
When I start the application I go to the browser the text box and the button appear. But in the browser console I'm getting these errors:
client.js:1 Uncaught SyntaxError: Unexpected token <
ContentScript.js:112 Exception in onResRdy: TypeError: Cannot read
property 'htmlRes' of undefined
localhost/:1 Unchecked runtime.lastError: Could not establish
connection. Receiving end does not exist.
I guess my problem is the linking between the 3 files but I tried several things and I can't solve the problem. I'm sure it's a stupid error but forgive me I'm just getting start. Any advice?
The browser (because you have <script src="/client.js">) makes a request for /client.js
The server:
Gets the request
Runs response
Reads index.html
Sends it to the browser
Since index.html starts with <, the browser throws an error when it tries to run it as JavaScript.
Why are you giving the browser index.html when it asks for client.js?
You need to examine the request object, determine what URL is being asked for, write logic to return the correct resource with the correct status code and the correct content-type, and then return that to the client.
The Node.js documentation has an example of this but you should probably stop trying to use createServer directly — since it involves a massive amount of wheel reinvention — switch to using Express and work through the (very short) getting started guide which includes a section on using the static module to serve up static files.
I'm starting with Node.js and I have already a problem in my first program. Below is the code I'm using. Index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Random Temperatures</title>
</head>
<body>
<input type="text" id="tb" name="tb" />
<input type="button" value="Random Number!" id="myButton" name="myButton"/>
<script src="client.js"></script>
</body>
</html>
Client.js:
const textBox = document.getElementById('tb');
const button = document.getElementById('myButton');
button.addEventListener('click', function(e) {
var rnd = Math.floor(Math.random() * 100);
textBox.value = rnd;
});
Server.js:
var app = require('http').createServer(response);
var fs = require('fs');
app.listen(8080);
console.log("App running…");
function response(req, res) {
fs.readFile(__dirname + '/public/index.html',
function (err, data) {
if (err) {
res.writeHead(500);
return res.end('Failed to load file index.html');
}
res.writeHead(200);
res.end(data);
});
}
When I start the application I go to the browser the text box and the button appear. But in the browser console I'm getting these errors:
client.js:1 Uncaught SyntaxError: Unexpected token <
ContentScript.js:112 Exception in onResRdy: TypeError: Cannot read
property 'htmlRes' of undefined
localhost/:1 Unchecked runtime.lastError: Could not establish
connection. Receiving end does not exist.
I guess my problem is the linking between the 3 files but I tried several things and I can't solve the problem. I'm sure it's a stupid error but forgive me I'm just getting start. Any advice?
The browser (because you have <script src="/client.js">) makes a request for /client.js
The server:
Gets the request
Runs response
Reads index.html
Sends it to the browser
Since index.html starts with <, the browser throws an error when it tries to run it as JavaScript.
Why are you giving the browser index.html when it asks for client.js?
You need to examine the request object, determine what URL is being asked for, write logic to return the correct resource with the correct status code and the correct content-type, and then return that to the client.
The Node.js documentation has an example of this but you should probably stop trying to use createServer directly — since it involves a massive amount of wheel reinvention — switch to using Express and work through the (very short) getting started guide which includes a section on using the static module to serve up static files.
I'm starting with Node.js and I have already a problem in my first program. Below is the code I'm using. Index.html:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Random Temperatures</title>
</head>
<body>
<input type="text" id="tb" name="tb" />
<input type="button" value="Random Number!" id="myButton" name="myButton"/>
<script src="client.js"></script>
</body>
</html>
Client.js:
const textBox = document.getElementById('tb');
const button = document.getElementById('myButton');
button.addEventListener('click', function(e) {
var rnd = Math.floor(Math.random() * 100);
textBox.value = rnd;
});
Server.js:
var app = require('http').createServer(response);
var fs = require('fs');
app.listen(8080);
console.log("App running…");
function response(req, res) {
fs.readFile(__dirname + '/public/index.html',
function (err, data) {
if (err) {
res.writeHead(500);
return res.end('Failed to load file index.html');
}
res.writeHead(200);
res.end(data);
});
}
When I start the application I go to the browser the text box and the button appear. But in the browser console I'm getting these errors:
client.js:1 Uncaught SyntaxError: Unexpected token <
ContentScript.js:112 Exception in onResRdy: TypeError: Cannot read
property 'htmlRes' of undefined
localhost/:1 Unchecked runtime.lastError: Could not establish
connection. Receiving end does not exist.
I guess my problem is the linking between the 3 files but I tried several things and I can't solve the problem. I'm sure it's a stupid error but forgive me I'm just getting start. Any advice?
The browser (because you have <script src="/client.js">) makes a request for /client.js
The server:
Gets the request
Runs response
Reads index.html
Sends it to the browser
Since index.html starts with <, the browser throws an error when it tries to run it as JavaScript.
Why are you giving the browser index.html when it asks for client.js?
You need to examine the request object, determine what URL is being asked for, write logic to return the correct resource with the correct status code and the correct content-type, and then return that to the client.
The Node.js documentation has an example of this but you should probably stop trying to use createServer directly — since it involves a massive amount of wheel reinvention — switch to using Express and work through the (very short) getting started guide which includes a section on using the static module to serve up static files.
So I'm using a library called ng-webworker and attempting to run a very simple long running task.
$scope.onParallelDownload = function() {
function doubler(num) {
return num * 2;
}
var myWorker = webWorker.create(doubler);
myWorker.run(3).then(function(result) {
alert("Answer: " + result);
}, function(error) {
var err = error;
});
}
This works perfectly in Chrome and shows the alert, but when run in Internet Explorer 11, where I am debugging it the error function is hit, which was still promising, however, there is no data given in the error payload which is problematic because I've absolutely no idea what is causing the web worker to fail on that particular browser.
Most likely you did not set the path to the file worker_wrapper.min.js (or worker_wrapper.js). This file is required for IE (see below). Adjust your app config to the following:
angular.module('myApp', [
// your dependencies
'ngWebworker'
])
.config(['WebworkerProvider', function (WebworkerProvider) {
WebworkerProvider.setHelperPath("./bower_components/ng-webworker/src/worker_wrapper.min.js"); // adjust path
}]);
This code assumes you installed ngWebworker with bower. You might still have to adjust the path, depending on the path you are in.
If you've already set the helper path but it still does not work, check if helper file is being loaded in the developer tools (you might have set a wrong path and get a 404).
Details
When passing a function to Webworker, it transforms this function into a blob which is then executed by the web worker as if it were an independent file. However, Internet Explorer treats these blobs as cross-domain, so this does not work. The workaround that ngWebworker uses is to run an independent JavaScript file (the worker_wrapper.min.js we set above). The web worker then runs that file and ngWebworker passes your stringified function to the worker where it is evaluated.
Note that if you're not on IE, this file will not be used.
I managed to get a Web Worker (not a content/worker) in my Firefox add-on using the Add-on SDK. I followed Wladimir's advice here to get the Worker class working: Concurrency with Firefox add-on script and content script
Now, I can launch a worker in my code and can talk to it by sending/receiving messages.
This is my main.js file:
// spawn our log reader worker
var worker = new Worker(data.url('log-reader.js'));
// send and respond to some dummy messages
worker.postMessage('halo');
worker.onmessage = function(event) {
console.log('received msg from worker: ' + event.data);
};
This is my log-reader.js file:
// this function gets called when main.js sends a msg to this worker
// using the postMessage call
onmessage = function(event) {
var info = event.data;
// reply back
postMessage('hey addon, i got your message: ' + info);
if (!!FileReaderSync) {
postMessage('ERROR: FileReaderSync is not supported');
} else {
postMessage('FileReaderSync is supported');
}
// var reader = new FileReaderSync();
// postMessage('File contents: ' + reader.readAsText('/tmp/hello.txt'));
};
My problem is that the FileReaderSync class is not defined inside the log-reader.js file, and as a result I get the error message back. If I uncomment the last lines where FileReaderSync is actually used, I will never get the message back in my addon.
I tried using the same trick I used for Worker, by creating a dummy.jsm file and importing in main.js, but FileReaderSync will only be available in main.js and not in log-reader.js:
// In dummy.jsm
var EXPORTED_SYMBOLS=["Worker"];
var EXPORTED_SYMBOLS=["FileReaderSync"];
// In main.js
var { Worker, FileReaderSync } = Cu.import(data.url('workers.jsm'));
Cu.unload(data.url("workers.jsm"));
I figure there has to be a solution since the documentation here seems to indicate that the FileReaderSync class should be available to a Web Worker in Firefox:
This interface is only available in workers as it enables synchronous I/O that could potentially block.
So, is there a way to make FileReaderSync available and usable in the my Web Worker code?
Actually, your worker sends "ERROR" if FileReaderSync is defined since you negated it twice. Change !!FileReaderSync to !FileReaderSync and it will work correctly.
I guess that you tried to find the issue with the code you commented out. The problem is, reader.readAsText('/tmp/hello.txt') won't work - this method expects a blob (or file). The worker itself cannot construct a file but you can create it in your extension and send to the worker with a message:
worker.postMessage(new File("/tmp/hello.txt"));
Note: I'm not sure whether the Add-on SDK defines the File constructor, you likely have to use the same trick as for the Worker constructor.
The worker can then read the data from this file:
onmessage = function(event)
{
var reader = new FileReaderSync();
postMessage("File contents: " + reader.readAsText(event.data));
}