BroadcastChannel在setInterval中丢失.postMessage()上下文的问题排查与解决
Web Worker中FoozleTimer调用emit()报错
this.channel.postMessage is not a function 问题详情
调用Web Worker中的FoozleTimer类的emit()方法时,抛出以下错误:
Uncaught TypeError: this.channel.postMessage is not a function
调试确认:
- 构造函数注入的
this.channel是有效的BroadcastChannel实例 emit()方法中该对象确实包含postMessage()方法- 已使用箭头函数确保
setInterval的this指向FoozleTimer类,但问题依旧
相关代码如下:
FoozleTimer类代码
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;
Web Worker代码
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') } }
调用Web Worker的代码
const worker = new Worker("./TimerWorker2.js", { type: "module" });
原因分析
问题出在类方法emit的this绑定上:虽然setInterval中用箭头函数确保了调用this.emit()时的this指向FoozleTimer实例,但在Web Worker的ES模块模式下,类方法的this绑定可能因引擎的特殊处理出现意外丢失,导致调用this.channel时的上下文异常,进而触发postMessage不是函数的错误。
解决方案
有两种可靠的解决方式:
方案1:将emit定义为箭头函数属性
把emit从类方法改为类的箭头函数属性,让this永久绑定到FoozleTimer实例,不受调用方式影响:
class FoozleTimer { timerInterval:any; channel:BroadcastChannel; constructor(channel:BroadcastChannel) { this.timerInterval = null; this.channel = channel; } // 改为箭头函数属性 emit = () => { this.channel.postMessage({countBy:1}) console.log('emit') return ''; } startTimer(intervalTime = 1000) { this.timerInterval = setInterval(() => this.emit(), intervalTime); } // 其他方法不变... }
方案2:在构造函数中绑定emit的this
在类的构造函数中,手动将emit方法的this绑定到当前实例:
class FoozleTimer { timerInterval:any; channel:BroadcastChannel; constructor(channel:BroadcastChannel) { this.timerInterval = null; this.channel = channel; // 绑定this到实例 this.emit = this.emit.bind(this); } emit() { this.channel.postMessage({countBy:1}) console.log('emit') return ''; } // 其他方法不变... }
验证
修改后重新运行代码,emit()方法将能正常调用this.channel.postMessage(),定时器会按预期向BroadcastChannel发送消息。
内容的提问来源于stack exchange,提问作者ServerStorm
相关产品推荐
相关产品推荐

