What is MessageChannel
MessageChannel allows two different scripts running in different browser contexts of the same document (e.g. two iframes, a document body and an iframe, two documents using SharedWorker
, or two workers) to communicate directly , using a port at each end to pass messages to each other over a bidirectional channel.
MessageChannel sends messages in the form of DOM Event
, so it is an asynchronous macro task.
Basic usage
- Use the
MessageChannel()
constructor to create a communication channel and get two port MessagePort objectsport1
port2
; - One port uses
postMessage
to send messages, and the other port usesonmessage
to receive messages; - Another port receives messages via
onmessage
; - When the port receives a message that cannot be deserialized, use
onmessageerror
to process; - When you stop sending messages, call
close
to close the port;
method one
const { port1, port2 } = new MessageChannel();
port1.onmessage = (event) => {
console.log('收到来自port2的消息:', event.data);
};
port1.onmessageerror = (event) => {};
port2.onmessage = function (event) {
console.log('收到来自port1的消息:', event.data);
port2.postMessage('我是port2');
};
port2.onmessageerror = (event) => {};
port1.postMessage('我是port1');
Method 2
const { port1, port2 } = new MessageChannel();
port1.addEventListener('message', event => {
console.log('收到来自port2的消息:', event.data);
});
port1.addEventListener('messageerror', (event) => { });
port1.start();
port2.addEventListener('message', event => {
console.log('收到来自port1的消息:', event.data);
port2.postMessage('我是port2');
});
port2.addEventListener('messageerror', (event) => { });
port2.start();
port1.postMessage('我是port1');
In the above two ways, the output is:
Received message from port1: I am port1
Received message from port2: I am port2
- Using the
addEventListener
method, you need to manually callstart()
method message to flow, because the initialization is suspended.onmessage
has implicitly called thestart()
method.
Execution order in Event Loop
Sync Tasks > Micro Tasks > requestAnimationFrame > DOM Rendering > Macro Tasks
setTimeout(() => {
console.log('setTimeout')
}, 0)
const { port1, port2 } = new MessageChannel()
port2.onmessage = e => {
console.log(e.data)
}
port1.postMessage('MessageChannel')
requestAnimationFrame(() => {
console.log('requestAnimationFrame')
})
Promise.resolve().then(() => {
console.log('Promise1')
})
The output is:
Promise // 微任务先执行
requestAnimationFrame
setTimeout // 宏任务,先定义先执行
MessageChannel // 宏任务,后定义后执行
requestAnimationFrame - a task that is not a macro task
window.requestAnimationFrame() tells the browser that you want to perform an animation and asks the browser to call the specified callback function to update the animation before the next repaint. This method needs to pass in a callback function as a parameter, the callback function will be executed before the browser's next repaint --- MDN
Strictly speaking, raf is not a macro task because
- The execution timing and macro tasks are completely inconsistent;
- When the raf task queue is executed, all tasks in the queue will be executed;
scenes to be used
One: contextual communication of the same document
var channel = new MessageChannel();
var para = document.querySelector('p');
var ifr = document.querySelector('iframe');
var otherWindow = ifr.contentWindow;
ifr.addEventListener("load", iframeLoaded, false);
function iframeLoaded() {
otherWindow.postMessage('Hello from the main page!', '*', [channel.port2]);
}
channel.port1.onmessage = handleMessage;
function handleMessage(e) {
para.innerHTML = e.data;
}
Two: Combined with Web Worker to achieve multi-threaded communication
Three: deep copy
Most scenarios that require deep copying use JSON.parse(JSON.stringify(object))
. But this approach ignores undefined, function, symbol and circularly referenced objects .
// 深拷贝函数
function deepClone(val) {
return new Promise(resolve => {
const { port1, port2 } = new MessageChannel()
port2.onmessage = e => resolve(e.data)
port1.postMessage(val)
})
}
The deep copy implemented by MessageChannel can only solve the problem of undefined and circular reference objects, and it is still helpless for Symbol and function.
practice
Problem description :
There are two methods called by a third party in an indeterminate order. After both methods are called, further processing is performed.
Solution <br>Use setTimeout to simulate the calling sequence of the method, the code is as follows:
const { port1, port2 } = new MessageChannel()
let data
const handleUser = newData => {
if (data) {
const result = { ...data, ...newData }
console.log('获取到全部数据', result)
port1.close()
port2.close()
} else {
data = newData
}
}
const getName = () => {
const params = { name: '123' }
port1.postMessage(params)
port1.onmessage = e => {
handleUser(e.data)
}
}
const getAge = () => {
const params = { age: 88 }
port2.postMessage(params)
port2.onmessage = e => {
handleUser(e.data)
}
}
setTimeout(() => {
getName()
}, 0)
setTimeout(() => {
getAge()
}, 10)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。