Reputation: 99
This timing class wired from a Web Worker does not retain context for the BroadcastChannels postMessage() method. When in use it throws a error:
Uncaught TypeError: this.channel.postMessage is not a function
The error occurs when the emit() method calls the postMessage() method. Upon checking in the debugger, I can confirm that the this.channel object injected into the constructor is a valid BroadcastChannel instance and is also valid within the emit() method, as it contains the postMessage() method. However, despite these valid conditions, the error is still thrown.
I am using an arrow function so the this
would respect the parent (FoozleTimer class) of the setInterval(); however it does not seem to work.
Here is the timing class, please note the .startTimer() and .emit() methods:
class FoozleTimer {
timerInterval:any;
channel:BroadcastChannel;
/**
* Construct
*/
constructor(channel:BroadcastChannel) {
this.timerInterval = null;
this.channel = channel;
}
/**
* Time interval callback function
*/
emit() {
this.channel.postMessage({countBy:1})
console.log('emit')
return '';
}
startTimer(intervalTime = 1000) {
this.timerInterval = setInterval(() => this.emit(), intervalTime);
}
stopTimer() {
clearInterval(this.timerInterval);
}
getTimer() {
return this.timerInterval;
}
}
export default FoozleTimer;
Here the Web Worker where this timer is called:
const channel = new BroadcastChannel('channel2');
const controls = new BroadcastChannel('controls');
// Pass the Broadcast Channel into the timer
const FT = new FoozleTimer(channel);
FT.startTimer(1000);
const timer = FT.getTimer();
controls.onmessage = (event) => {
if(event && event.data.msg == 'kill-timer') {
FT.stopTimer();
console.log('killed timer')
}
}
The web worker is called like this in an index.html file:
const worker = new Worker("./TimerWorker2.js", { type: "module" });
Can you provide any insights as to why this wrong and potential ways to resolve it?
Upvotes: 0
Views: 607
Reputation: 99
I was able to fix this by using a closure instead of of the emit() method. I then set the context of this to a parameter self and can use this inside the closure. I also wrapped the BroadCast channel into a class wrapper so its' context couldn't change.
Here is the new timer class:
import { BroadcastService } from "./BroadcastService";
class FoozleTimer {
timerInterval:any;
broadcastService:BroadcastService;
/**
* Construct
*/
constructor(broadcastService:BroadcastService) {
this.timerInterval = null;
this.broadcastService = broadcastService;
}
startTimer(intervalTime = 1000) {
const self:any = this;
this.timerInterval = setInterval(function() {
self.broadcastService.postMessage({countBy:1, status:"pass"})
}, intervalTime);
}
stopTimer() {
clearInterval(this.timerInterval);
}
getTimer() {
return this.timerInterval;
}
}
export default FoozleTimer;
And here is the BroadcastService:
export class BroadcastService {
broadcastChannel: BroadcastChannel;
constructor(name:string) {
this.broadcastChannel = new BroadcastChannel(name);
}
postMessage(msg:any) {
this.broadcastChannel.postMessage(msg);
}
getBroadcastChannel() {
return this.broadcastChannel;
}
}
I will try to refactor the BroadcastService to not have the coupled BroadcastChannel(name) and instead inject a BroadcastChannel instance.
I hope this helps someone else
Upvotes: 0