Angular中使用postMessage与新开弹窗通信出现竞态条件如何解决?
解决方案
核心逻辑修正
将原有的「父窗口主动发INIT触发弹窗响应」的逻辑反转,由弹窗完成资源加载、事件监听注册完成后,主动向父窗口(window.opener)发送就绪信号,父窗口收到信号后再发送业务消息,从根源消除竞态条件。父窗口的message监听会在弹窗加载完成前完成注册,因此弹窗主动发送的READY信号100%能被父窗口捕获,不存在消息丢失的情况。
修正后代码
父窗口代码
删除不可靠的setTimeout逻辑,保留origin安全校验,同时补全原代码的语法错误:
const windowFeatures = "height=800,width=1280,toolbar=1,menubar=1,location=1"; const printWindow = window.open("/print", "Print_Me", windowFeatures); // 兜底判断:弹窗被浏览器拦截时的处理 if (!printWindow) { alert("弹窗被浏览器拦截,请调整权限后重试"); } window.addEventListener("message", (event) => { if(event.origin !== window.origin) return; if(event.data.printPageStatus === "READY") { printWindow.postMessage({message: "from parent"}, window.origin); return; } });
弹窗代码
删除等待INIT的逻辑,页面/组件就绪后直接向父窗口发READY信号:
constructor() { window.addEventListener("message", event => { if(event.origin !== window.origin) return; if(event.data.message === "from parent") { this.processMessages(event.data); } }, false); } // 选择对应技术栈的就绪触发时机即可: // 原生JS:绑定到 window.onload / DOMContentLoaded 事件 // Vue:写在 mounted 钩子中 // React:写在空依赖的 useEffect 中 onReady() { window.opener.postMessage({printPageStatus: "READY"}, window.origin); }
额外优化建议
- 增加超时兜底:父窗口打开弹窗后启动定时器,若10s内未收到READY信号,判定为弹窗加载失败,关闭弹窗并提示用户
- 消息唯一标识:若同时打开多个弹窗,可在打开时生成唯一ID传递给弹窗,弹窗回传READY时携带ID,避免多个弹窗的消息混淆
- 销毁清理:父窗口在收到业务处理完成通知后,移除对应的
message监听,避免内存泄漏
内容的提问来源于stack exchange,提问作者Zeo
相关产品推荐
相关产品推荐

