一般 postMessage 用于不同页面窗口间通信,但在同一个页面内也是可以使用的。
window.addEventListener('message', e => {
if (
e.origin === location.origin &&
e.source === e.target &&
e.data === 'test'
) {
console.log(2)
}
})
出于安全考虑,判断是当前页面内(同一个页面同一个窗口)才执行相关逻辑:
e.origin === location.origin同源e.source === e.target同窗口
console.log(1)
window.postMessage('test', location.origin)
console.log(3)
postMessage 第二个参数是 targetOrigin,消息也只发送给同源页面。
因为 postMessage 是宏任务,所以打印顺序是 1、3、2 。
那想按顺序打印 1、2、3 怎么办,中间加一个宏任务等待。
window.addEventListener('message', e => {
if (
e.origin === location.origin &&
e.source === e.target &&
e.data === 'test'
) {
console.log(2)
}
})
console.log(0)
window.postMessage('test', location.origin)
console.log(1)
await new Promise(r => setTimeout(r))
console.log(3)
await new Promise(r => setTimeout(r)) 一行代码实现宏任务等待,setTimeout 也是宏任务,执行以上代码时,任务队列中 setTimeout 在 postMessage 后面,利用 await 和 Promise 实现等待。
另一种变体:postMessage 也可以放到 Promise 里,效果是一样的。
console.log(1)
await new Promise(r => {
window.postMessage('test', location.origin)
setTimeout(r)
})
console.log(3)
其实还可以有一种变体,既然 postMessage 是宏任务,那也可以不需要 setTimeout,只通过 postMessage 来实现宏任务等待,因为是同一个窗口,可以借助 window 全局变量来搭个桥,存一下 Promise 的 resolve,消息接收方在合适的时机执行这个回调。
window.addEventListener('message', e => {
if (
e.origin === location.origin &&
e.source === e.target &&
e.data === 'test'
) {
console.log(2)
if (typeof window.waitResolve === 'function') {
window.waitResolve() // Promise 的 resolve
delete window.waitResolve
}
}
})
console.log(1)
await new Promise(r => {
window.waitResolve = r
window.postMessage('test', location.origin)
})
console.log(3)
顺序打印 1、2、3
以上!