When I issue webworker.postMessage('any message'), it gets processed twice by the onMessage listener. I tried running stopPropagation on the message, but it still gets run again. I verified by console.log that the call to postMessage in the main thread only gets called once. I verified that the webworker being called is unique. Can I fix it so that it postMessage only results in one onMessage event being called?
Snippet showing call to web worker:
this.webworker.postMessage('Message one');
}
My web worker:
/// <reference lib="webworker" />
onmessage = function(data) {
console.log('## in web worker' + JSON.stringify(data.data));
data.stopPropagation();
}
Something similar happened also to me. I can't explain why, even I did some test. Anyway my only contribute is that the same somewhat buggy script gave me the doubled console.log message only in Firefox, not in Edge, nor Chrome.
Anyhow in my simple test worker I set a counter. This way I can confirm that the execution happens only once, and the issue is only related to the console.log.
self.curcounter=self.curcounter+ 1; //this has been executed once
self.console.log('WORKER received a INT:' + e.data + ' self.curcounter: '+ self.curcounter); //this twice
Related
Investigating a strange bug for which the stack trace (on Firefox 87) points to the line where the onmesssage handler is assigned:
const worker = new Worker(/* URI */);
worker.onmessage = msg => { // stacktrace points here
let trx = JSON.parse(msg.data);
// ...
}
// more init code
The calls in the stacktrace leading into this spot match the context in which the worker is created and onmessage assigned, but the calls after this spot in the stack trace make it seem like the handler function is called synchronously when assigned.
The worker itself connects to the server and can push messages to the main thread, without the main thread first having to post messages to it. It's thus entirely possible for a new message to be there before the onmessage assignment is executed. However, I've been unable to reproduce the behavior. It seems messages posted before the handler is assigned are discarded.
Short of a race condition in Firefox itself, is there anything else that could be going on?
(The race in question here is different from this similar question)
I am using face-api.js to detect faces in a given image. Everything is configured as mentioned on Github and detection is working.
But I want to call faceapi.detectAllFaces() asynchronously, I means I don't want to wait for its result, so I can't call await faceapi.detectAllFaces(). I tried below code:
In cam.js
async function detectFace()
{
document.getElementById('camPic').src = getCurrentImageAsBase64(); //getCurrentImageAsBase64() gets the image in base64 format from canvas
return faceapi.detectAllFaces(document.getElementById('camPic'), new faceapi.SsdMobilenetv1Options());
}
In index.jsp
$('#camPrcd').click(function(e)
{
e.preventDefault();
detectFace().then((arr) => console.log(arr));
console.log('detection called');
});
After button click, on console I can see message "detection called" and after sometime detection result gets logged from then() block.
But my observation is when detectFace() is called the html page feels like hanged (not able to click on any button) and when arr gets printed on console then I can able to click on page.
Looks like even though "detection called" message gets printed immediately before detection happens but faceapi.detectAllFaces() is not doing work asynchronously.
Note: This happens only for first call of faceapi.detectAllFaces() as per author of face-api.js during first call model for face detection get compiled and so detection time is more at first call as compare to subsequent call to function.
So is there any way that, I can call for detection and still web page is accessible and when it's finished a callback function will handle the detection result.
In my ServiceWorker I have the following code:
function send_alert() {
console.log('alert');
};
self.addEventListener('message', (evt) => {
console.log("message:" + evt.data);
self.setTimeout(1000*5, send_alert);
console.log("done");
});
If a button is clicked in the main page, a message is send to the worker. This works fine and I see the message: ... and done logs in the console. The self.setTimeout call does not raise an error, but send_alert is never called.
According to https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope implements WindowTimers which implements setTimeout. self should be an instance of ServiceWorkerGlobalScope, so I don't see the problem. What's the proper way to use setTimeout in a service worker?
Supposed the self.setTimeout function is the normal setTimeout function, then the order of the parameters is wrong: You first have to provide the callback, then the number of milliseconds.
This is, unfortunately, counterintuitive to what's seen as de-facto standard in JavaScript today, but setTimeout is pretty old, so… historical reasons 😉
I initiated a web worker on chrome and it had a simple function that was called repeatedly using setTimeout. Surprisingly the web worker terminated after the function was called around 1000 times. Can anyone explain why? I guess chrome is doing some optimization.
webworker.js
function hi() {
postMessage('1');
setTimeout(hi, 1);
}
hi();
main.js
var blob = new Blob([code]);
var blobURL = window.URL.createObjectURL(blob);
var worker = new Worker(blobURL);
worker.onmessage = function(data) {
console.log(data.data); // gets called around 1000 times and done
};
EDIT:
Reproduced in a fiddle:
http://jsfiddle.net/meovfpv3/1/
It seems to takes arbitrarily long for the onmessage callback to stop firing, as quickly as a few seconds and as long as +5 mins
Here is my best guess at what is happening. By posting a message from the Web Worker every 1ms, you are demanding that the main thread processes each posted message within 1ms.
If the main thread isn't able to process the message within 1ms, you are still sending it a new message even though it isn't finished processing the last message. I would imagine this puts it into a queue of messages waiting to be processed.
Now since you are sending messages from the web worker faster than they can be processed, this queue of unprocessed messages is going to get bigger and bigger. At some point Chrome is going to throw up its hands and say "There are too many messages in the queue", and instead of queueing new messages for processing, it drops them.
This is why if you use a reasonable number in your timeout like 100ms, the message has plenty of time to be processed before the next message is sent, and no problem with unprocessed messages occurs.
I've created a jsFiddle where the worker sends a message to the main thread, and the main thread sends the message back to the worker. If that process doesn't happen before the next message is sent, the counters in both threads will be mismatched and the web worker will terminate.
http://jsfiddle.net/meovfpv3/3/
You can see that with a reasonable setTimeout of 100ms, all messages have adequate time to process before the next message occurs.
When you lower the setTimeout to 1ms, the message chain doesn't have time to finish before the next message is sent and the counters in each thread become eventually desynced, tripping the if clause and terminating the web worker.
One way to fix this problem is instead of blindly posting a message every 1ms whether the last one has been processed or not, only post a new message after you have received a message back from the main thread. This means that you are only posting messages as fast as the main thread can process them.
For completeness here is a copy of the JSFiddle code:
Worker:
var counter2 = 0;
var rcvd = true;
function hi() {
counter2++;
console.log("")
console.log("postMessage", counter2)
postMessage(counter2);
if (!rcvd) {
self.close();
console.log("No message received");
}
rcvd = false;
setTimeout(hi, 1);
}
hi();
onmessage = function(e) {
rcvd = true;
console.log("secondMessage", e.data);
}
Main:
var ww = document.querySelector('script[type="text/ww"]'),
code = ww.textContent,
blob = new Blob([code], {type: 'text/javascript'}),
blobUrl = URL.createObjectURL(blob),
worker = new Worker(blobUrl),
counter = 0;
worker.onmessage = function(e) {
counter++;
console.log("onmessage:", counter);
worker.postMessage(e.data);
}
Firstly, a couple of observations, which I cannot explain but are kind of interesting and might be inspirational for someone:
#Anson - If I put your jsFiddle code into Codepen (still in Chrome) there are no problems there. The onmessage callback just keeps working!
And back in jsFiddle... It fails even changing the setTimeout to a long gap like 10s so it's not the number of times that the worker posts a message, it's how long before the onmessage callback stops firing – which has a lot of variance.
Then I found some ways to keep the onmessage handler alive in this specific example:
Add a button/link in the html and a handler (I used jQuery) that will terminate the worker on click. Just adding this code fixes it. $("#stop").on("click",function(e){e.preventDefault();worker.terminate();});
Just add console.log(worker) after defining onmessage.
Inspired by an answer posted in the related question you can also simply add window.worker = worker after defining onmessage.
Something about mentioning worker again in all cases seems to keep it alive.
Are you trying to postMessage every 1ms? Then you probably meant to use setInterval():
setInterval(function(){
postMessage('1');
}, 1);
Edit: I incorrectly saw recursion which wasn't there, just because I was looking for it. I would still use setInterval over setTimeout though.
I have created a Firefox extension, which has iframe in the popup.html.
To pass messages between iframe and main.js, I'm using postMessage method.
The popup is behaving weird when receiving message. I used console.log to get the message which is being received. The message gets console.log that many times, the times extension was clicked.
For e.g. if I had open the extension 4 times, the iframe passes the message only once, but the message gets receied 4 times.
To make you understand I'm writing step by step process:
Open a tab, and click on extension.
A message is passed from iframe to the main.js, which after receiving message, console.logs the same message. Now this message appears once.
Click anywhere to close extension. Click on extension again > message is passed > console.logs the same message twice.
Repeat this process, and it logs the message those many times, the extension is clicked.
Is this a bug with Firefox? Or I'm doing something wrong?
EDITED:
Here is some snippet from my main.js:
if (self.port !== undefined){
self.port.on("show", function(a,b) {
myApp.initialize(a,b);
});
}
var myApp = {
initialize: function (a,b){
window.addEventListener ("message" , receiveMessage, false);
function receiveMessage(event){
console.log("event.data - - - - - - ",event.data);
}
},
goAbc : function(){
self.port.emit("close");
}
}
The code in iframe is:
$(document).ready(function(){
parent.postMessage("getData", "*");
});
EDITED: One more question:
If I'm calling "myApp.goAbc()" somewhere in the code, it says, self.port is undefined. What's the issue here? Should I use something else in place of self.port?
With each "show", you call myApp.initialize, which addEventListener a new event listener. So the first time, there is one, the next time there will be another, additional one (as you call addEventListener again) for a total of two, and so on.
Instead, you need to call addEventListener exactly once and not on each "show".