事件待处理时添加新监听器的行为及Web Worker场景疑问
Web Worker事件待处理时添加新监听器的行为
先给你明确结论:当Worker的message事件已经触发并进入待处理的任务队列,但还没执行完现有监听器时,此时新增的同类型监听器不会被这次事件触发。
背后的逻辑
浏览器的事件循环机制是这样的:当事件(比如Worker的message)触发时,浏览器会把该事件对应的所有已注册监听器的回调任务,一次性加入到任务队列中。在当前同步代码或者正在执行的任务过程中新增的监听器,不会被回溯加入到这次事件的执行队列里——它只会对之后触发的同类型事件生效。
结合你的场景分析
你的流程大概是:
- 给
_worker绑定第一个message监听器,负责设置window["test"] = event.data - 在Promise里检查
window["test"],如果没设置就绑定第二个监听器,用来resolve这个Promise
这里可能会遇到的坑:
如果Worker的message事件已经触发,但因为事件循环的任务顺序(比如当前同步代码还在运行,事件回调排在队列后面),第一个监听器还没执行,此时window["test"]还是未定义的。这时候你绑定了第二个监听器,但这次已经触发的message事件不会触发它,你的Promise就会一直处于pending状态,除非Worker再发送一次新的message。
给你的解决方案
与其依赖检查window["test"]这种容易出竞态的方式,不如直接用Promise封装Worker的message监听,利用addEventListener的once选项确保监听器只执行一次,从根源避免问题:
// 封装一个获取Worker首次消息的Promise函数 function waitForWorkerFirstMessage(worker) { return new Promise(resolve => { worker.addEventListener('message', (event) => { window["test"] = event.data; resolve(event.data); }, { once: true }); // 这个选项会让监听器执行一次后自动移除 }); } // 使用示例 waitForWorkerFirstMessage(_worker).then(messageData => { // 这里既能拿到消息数据,window["test"]也已经设置好了 console.log('Worker消息:', messageData); });
这样不管事件触发的时机如何,Promise都会准确捕获到Worker的第一次message事件,不会出现漏监的情况。
内容的提问来源于stack exchange,提问作者prmph
相关产品推荐
相关产品推荐

