Your HTML page (the host page) has an Iframe. The Iframe source is on other domain, so you will have to use the window.postMessage() for cross-origin communication.
But this communication is only in 1 direction (The Iframe can inform the Host or the Host can inform the Iframe)
This article shows you how to make the Request/Reply communication using postMessage() and MessageChannel combination.
The article did not show you how to add a timeout to the communication, so the Iframe might wait forever if the Host did not response.
I will resume the technique by some codes snippets and at the same time add the missing timeout implementation.
In this example:
- the Iframe want to ask the Host a
IframeConfigobject, - the Iframe open a new
MessageChannelto post aIframeConfigRequestobject to the Host. - when the Host received this
IframeConfigRequest, it will reply theIframeConfigobject on theMessageChannel - we want the Promise to be terminated in timeout if the host did not response, so we make it race with a "timeout" promise. => what ever terminate first will solve the promise.
function requestIframeConfig(uuid: string): Promise<IframeConfig> {
const mainPromise = new Promise<IframeConfig>((res, rej) => {
const channel = new MessageChannel();
//setup listener to the response
channel.port1.onmessage = ({ data }) => {
channel.port1.close();
const response = data as IframeConfig|Error
if (response instanceof Error) {
rej(response);
}
else if (!response.type) {
rej(new Error(`Unable to get IframeConfig from the sdk. Invalid response: missing type`))
}
else {
res(response);
}
};
//send the request
const request: IframeConfigRequest = {
type: MessageType.IframeConfigRequest,
uuid
}
parent.postMessage(request, "*", [channel.port2])
});
return Promise.race([
mainPromise,
new Promise<IframeConfig>((_, reject) =>
setTimeout(() => reject(new Error('Timeout: Unable to get IframeConfig from the sdk within 10 seconds')), 10000)
)
]);
}let iframeConfig: IframeConfig;
window.addEventListener(
"message",
function (event: MessageEvent<IframeConfigRequest>) {
if (event.data.type != MessageType.IframeConfigRequest) return; //received non-concerned event
//response to the iframe
event.ports[0].postMessage(iframeConfig);
}
);
You can change
to
since reject is already a function, and the
setTimeoutfunction passes any arguments aftertimeoutMsto whatever function is marked.setTimeoutis funny that way. Also, is there a reason you use res/rej in the first promise, but reject in the timeout one? It's usually nice to stay consistent (I'm partial to keeping the full names). Outside of that, nice reference.