如何在Promise的then回调中向iframe发送postMessage获取用户连接信息?
在Promise的then回调中向iframe父页面发送postMessage并同步连接用户数据
问题背景
我有一段基于Promise的WebRTC音视频应用代码,目前通过带定时器的函数循环向父页面(嵌入该WebRTC页面的Flask应用)发送postMessage,但希望能直接在Promise的then回调中发送状态,同时需要实时获取当前连接用户的相关数据,用来控制Flask应用里的功能锁定/解锁。
现有代码
const socket = io('/') const videoGrid = document.getElementById('video-grid') const myPeer = new Peer(undefined, { path: '/peerjs', host: '/', port: '3000' }) const peers = {} let myVideoStream; const myVideo = document.createElement('video') myVideo.muted = true; function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } async function brodcastStatus(postMsg) { for (let i = 0; i < 10; i++) { parent.postMessage(postMsg, "*"); console.log(postMsg) await sleep(1500) } } if (KIND === 'server'){ Promise.resolve(navigator.mediaDevices.getUserMedia({ video: { width: 1920, height: 1080, aspectRatio: { ideal: 1.7777777778 }, frameRate: { max: 24, } }, audio: true })).then(stream => { brodcastStatus("server | camera | mic") myVideoStream = stream; myPeer.on('call', call => { call.answer(stream) const video = document.createElement('video') call.on('stream', userVideoStream => { addVideoStream(video, userVideoStream) }) }) socket.on('user-connected', userId => { connectToNewUser(userId, stream) }) }).catch(() => { Promise.resolve(navigator.mediaDevices.getUserMedia({ video: { width: 1920, height: 1080, aspectRatio: { ideal: 1.7777777778 }, frameRate: { max: 24, } }, audio: false })).then(stream => { brodcastStatus("server | camera | no mic") myVideoStream = stream; myPeer.on('call', call => { call.answer(stream) const video = document.createElement('video') call.on('stream', userVideoStream => { addVideoStream(video, userVideoStream) }) }) socket.on('user-connected', userId => { connectToNewUser(userId, stream) }) }).catch(() => { console.log("nocamera or microphone") }) }) }else if (KIND === 'client'){ Promise.resolve(navigator.mediaDevices.getUserMedia({ video: false, audio: true })).then(stream => { brodcastStatus("client | no camera | mic") myVideoStream = stream; myPeer.on('call', call => { call.answer(stream) const video = document.createElement('video') call.on('stream', userVideoStream => { addVideoStream(video, userVideoStream) }) }) socket.on('user-connected', userId => { connectToNewUser(userId, stream) }) }).catch(() => { console.log("Client") newStream = new MediaStream(); stream = newStream; myVideoStream = stream; myPeer.on('call', call => { call.answer(stream) const video = document.createElement('video') call.on('stream', userVideoStream => { addVideoStream(video, userVideoStream) }) }) socket.on('user-connected', userId => { connectToNewUser(userId, stream) }) }) } socket.on('user-disconnected', userId => { if (peers[userId]) peers[userId].close() }) myPeer.on('open', id => { socket.emit('join-room', ROOM_ID, id) }) function connectToNewUser(userId, stream) { const call = myPeer.call(userId, stream) const video = document.createElement('video') call.on('stream', userVideoStream => { addVideoStream(video, userVideoStream) }) call.on('close', () => { video.remove() }) peers[userId] = call } function addVideoStream(video, stream) { video.srcObject = stream video.setAttribute("style", "object-fit:cover; background: transparent url(/images/New_DACL_Logo.png) no-repeat center center; background-size: auto; overflow: hidden;") video.addEventListener('loadedmetadata', () => { }) videoGrid.append(video) }
解决方案
1. 在Promise的then回调中直接发送结构化消息
不需要循环定时发送,在媒体流获取成功的then回调里直接发送结构化数据(用对象代替字符串,方便父页面解析),同时包含当前连接用户数:
// 替换原有brodcastStatus调用,比如在server端带音视频的then回调中: .then(stream => { // 发送结构化状态数据 parent.postMessage({ type: 'device_status', role: 'server', hasCamera: true, hasMic: true, connectedUsers: Object.keys(peers).length }, 'https://your-flask-domain.com'); // 替换为你的Flask应用域名,不要用*提升安全性 myVideoStream = stream; // ...原有逻辑 })
在其他then回调和catch分支中也做类似修改,比如server端仅摄像头的情况:
.then(stream => { parent.postMessage({ type: 'device_status', role: 'server', hasCamera: true, hasMic: false, connectedUsers: Object.keys(peers).length }, 'https://your-flask-domain.com'); myVideoStream = stream; // ...原有逻辑 })
2. 实时同步连接用户数量
在用户连接和断开时,主动向父页面发送更新:
- 用户连接时:
socket.on('user-connected', userId => { connectToNewUser(userId, stream); // 发送用户数更新 parent.postMessage({ type: 'user_count', connectedUsers: Object.keys(peers).length }, 'https://your-flask-domain.com'); });
- 用户断开时:
socket.on('user-disconnected', userId => { if (peers[userId]) peers[userId].close(); delete peers[userId]; // 清理peers对象,保证计数准确 parent.postMessage({ type: 'user_count', connectedUsers: Object.keys(peers).length }, 'https://your-flask-domain.com'); });
3. 父页面(Flask应用)接收消息的处理逻辑
在Flask应用的页面中添加监听,根据收到的数据控制功能:
window.addEventListener('message', (event) => { // 验证消息来源,防止非法请求 if (event.origin !== 'https://your-webrtc-domain.com') return; const data = event.data; switch(data.type) { case 'device_status': // 处理设备状态,比如显示当前角色和设备情况 console.log(`角色:${data.role},摄像头:${data.hasCamera},麦克风:${data.hasMic}`); break; case 'user_count': // 根据连接用户数锁定/解锁功能 const targetBtn = document.getElementById('target-feature'); targetBtn.disabled = data.connectedUsers === 0; break; } });
注意事项
- 始终指定
postMessage的目标域名,不要用*,避免安全风险 - 父页面一定要验证消息来源,防止跨域恶意消息
- 结构化数据比字符串更易维护和解析,推荐使用对象格式传递信息
内容的提问来源于stack exchange,提问作者itIsWhatItIs
相关产品推荐
相关产品推荐

