.

Window. MessageChannel is an API for asynchronous operations that can be abstracted as a pipe, and since it is a pipe, it must have two ports that are information sources and message sources that can communicate with each other

Let’s start with compatibility

Compatibility sometimes determines whether or not you need to learn it and use it

As you can see, most major browsers already implement this API and can use it safely

Not many BB directly on the code to understand itbasisusage

Tip:

  • MessageChannel doesn’t have to be new to use it

  • Window and Worker objects also have corresponding postMessage methods, which can also listen for message events

  • Windows and workers handle dom2-level events without calling the start() method

const channel = new MessageChannel();
const port1 = channel.port1;
const port2 = channel.port2;
// Listen on events
// Use DOM2 level event processing
port1.addEventListener('message', (msg) => {
	console.log('Port1 receives message:' + msg.data);
})
port2.addEventListener('message', (msg) => {
	console.log('Port2 receives message:' + msg.data);
})
// Message events will not be heard if the start() method is not called
port1.start();
port2.start();


// With DOM0-level event handling, the start() method is implicitly called
//port1.onmessage = (msg)=>{
// console.log('port1 received message: '+ msg.data);
/ /}
//port2.onmessage = (msg)=>{
// console.log('port2 received message: '+ msg.data);
/ /}

port1.postMessage('Message from Port1');
port2.postMessage('Message from Port2');
Copy the code

Console output:

Port2 receives information: the information from port1 is index.html. Port1 receives information: the information from port2 is index.htmlCopy the code

Let’s look at the properties that a MessageChannel contains



read-only


Let’s take a look at the MSG from the listening event, what does it look like and what information does it contain

MessageEvent
bubbles
cancelBubble


We can think of familiar event objects like mouseEvents,

Focusing on the data and ports attributes,

  • data:postMessage()The first parameter passed in is actually this, used to carry the data being passed
  • ports:WorkerObject orwindowObject calledpostMessage()The second and third arguments passed in for transmissionMessagePortobject

Const {port1, port2} = new MessageChannel();

About the application scenarios of MessageChannel

  • MessageChannel provides a secure way to resolve cross-domain problems (primarily between iframes)
  • MessageChannel is supported with Web Workers
  • Vue’s nextTick downgrade scheme uses a MessageChannel

The postMessage method parameters used in the three scenarios differ as follows:

1. IFrame cross-domain scenarios

Assuming that the main page is index.html and the iFrame page is index2.html, the code is as follows:

//index.html
const {port1,port2} = new MessageChannel();
let iframe = document.querySelector('iframe');
iframe.addEventListener("load", () => {
	port1.onmessage = (e) = > {
		console.log(e)
	}
	iframe.contentWindow.postMessage('Information from index.html'.The '*', [port2]);
});

//index2.html
window.addEventListener('message', e=>{
  e.port[0].postMessage('Information from index2.html')});// You can even write it this way
//window.addEventListener('message', e => {
// const port2 = e.ports[0];
// port2.addEventListener('message', () => {
// port2.postmessage (' Message from index2.html ')
/ /});
// port2.start();
/ /});
// Or this will implicitly call the start() method
//window.addEventListener('message', e => {
// const port2 = e.ports[0];
// port2.onmessage = () => {
// port2.postmessage (' Message from index2.html ')
/ /};
/ /});
Copy the code

This may be confusing, but let’s get this straight. The postMessage and listen message examples I wrote in the beginning are both done by MessagePort on the MessageChannel object

That’s not the case here,

  • postMessage
    • inindex.htmlIs byiFrame.contentWindow That is –index2.htmlWindow object inGlobal method called
    • inindex2.htmlIn is created by port2That ise.port[0]Launched by the
  • onmessage
    • inindex.htmlIs listened on by port1
    • inindex2.htmlIs listened on by its Window object



postMessage()


  • Message: Here we pass message as a string, in fact we can pass any type, mounted to the data of the event object MessageEvent

  • TargetOrigin: Indicates the target source address

    • The default is/Is valid only for same-origin addresses
    • The value can be any URL, such as example.com
    • It can also be a wildcard*“, meaning broadcast to all iframes
  • Ports: Used to pass in interfaces and mount them to the ports of the event object MessageEvent

2.Web Worker

Suppose we have two Web Worker processes, js/w1.js and js/w2.js

The main page is index.html



Worker
postMessage()
message

Therefore, the communication between Worker thread and main thread is very convenient.

We mainly use new MessageChannel to realize the communication between the two Worker threads


//index.html
// Give birth to two babies first.
const w1 = new Worker('js/w1.js');
const w2 = new Worker('js/w2.js');
// Generate two ports
const{port1,port2} = new MessageChannel()

w1.postMessage(null, [port1])
w2.postMessage(null, [port2])

//w1.js
self.addEventListener('message',e=>{
	const port1 = e.ports[0]
	// Send information to another port
	port1.postMessage('Message from W1')
	port1.onmessage = e= >{
		console.log(e.data)
	}
})

//w2.js
self.addEventListener('message',e=>{
	const port2 = e.ports[0];
	// Send information to another port
	port2.postMessage('Message from W2')
	port2.onmessage = e= >{
		console.log(e.data)
	}
})
Copy the code

Console output

Information from W2 w1.js Information from W1 w2.jsCopy the code

Here’s the general idea:

  • index.html
    • It goes through hereWorkerthepostMessage()Method to push ports into eachWorkerIn the
  • W1. Js, w2. Js
    • Listening to themessageEvent, get the port that you pass to yourself

That’s it. You just need to send and receive data between port1 and port2

For example, if a data is sent from W1 to W2, the actual flow is W1 -> port1 -> port2 -> W2

It’s worth noting

  • What the Worker here callspostMessage()Different from iFrame’s parameters for cross-domain communication,
  • The second argument is passed inMessagePortRather thantargetOrigin

3. The Vue nextTick

Vue’s nextTick is so that the callback function can be executed to retrieve the latest DOM data after a DOM update,

After Vue source 2.5+, nextTick implementation has a separate JS file to maintain it, SRC /core/util/next-tick.js,

The author picked some key codes down to see:

import { isIOS, isNative } from './env'
let microTimerFunc
let macroTimerFunc

if (typeofsetImmediate ! = ='undefined' && isNative(setImmediate)) {
  macroTimerFunc = (a)= > {
    setImmediate(flushCallbacks)
  }
} else if (typeofMessageChannel ! = ='undefined' && (
  isNative(MessageChannel) ||
  // PhantomJS
  MessageChannel.toString() === '[object MessageChannelConstructor]'
)) {
  const channel = new MessageChannel()
  const port = channel.port2
  channel.port1.onmessage = flushCallbacks
  macroTimerFunc = (a)= > {
    port.postMessage(1)}}else {
  /* istanbul ignore next */
  macroTimerFunc = (a)= > {
    setTimeout(flushCallbacks, 0)}}// Determine microtask defer implementation.
/* istanbul ignore next, $flow-disable-line */
if (typeof Promise! = ='undefined' && isNative(Promise)) {
  const p = Promise.resolve()
  microTimerFunc = (a)= > {
    p.then(flushCallbacks)
    // in problematic UIWebViews, Promise.then doesn't completely break, but
    // it can get stuck in a weird state where callbacks are pushed into the
    // microtask queue but the queue isn't being flushed, until the browser
    // needs to do some other work, e.g. handle a timer. Therefore we can
    // "force" the microtask queue to be flushed by adding an empty timer.
    if (isIOS) setTimeout(noop)
  }
} else {
  // fallback to macro
  microTimerFunc = macroTimerFunc
}
Copy the code

That’s exactly what the website says

Next-tick. js declares two variables microTimerFunc and macroTimerFunc, which correspond to micro task functions and Macro task functions respectively. With macro Task implementations, native setImmediate is a feature supported only by advanced VERSIONS of IE and Edge, and native MessageChannel is tested first. If it is not supported, it is degraded to setTimeout 0; For micro Task implementations, we check if the browser natively supports Promise, and point directly to macro Task implementations if it doesn’t.

So nextTick has its own downgrading scheme for macro and micro tasks,

  • For macro tasks, the priority is setImmediate -> MessageChannel -> setTimeout 0

  • For microtasks, the priority is the Promise -> conversion to macro task execution

Specific can go to the official website to see vue.js technology revealed

At the end

MessageChannel can even implement an imperfect deep copy (an error occurs when the property is a function) for those interested, see link 1

HTML Living Standard MDN Web docs MessageChannel