javascript - Async/Await 在与 iframe 的跨域通信中回答 onMessage 事件
问题描述
我有一个 iframe,它正在与其父窗口通信,以通过 postMessage方法设置和获取一些必要的 cookie 。
首先,iframe 应用程序中的一个函数从父窗口请求 Cookie A。
function requestCommunication(topic, customerId) {
function cookieAvailable() {
return new Promise((resolve) => resolve(getCookieData('cookieName'));
});
}
console.log(cookieAvailable());
if(!!cookieAvailable()) {
//doStuff
}
}
cookieAvailable()
触发从 iframe 到 parent.window 的消息。反过来,窗口返回 cookie 及其数据作为字符串。这可以通过以下方式完成:
async function getCookieData(cookieName) {
const {data} = await new Promise(resolve => {
window.onmessage = (event) => {
resolve(event);
}
});
var cookieContent = JSON.parse(data);
var cookieContentData = JSON.parse(cookieContent.data);
return cookieContentData; //this returns the cookie data (in almost all cases)
}
我不知道如何正确使用承诺将其移交给我的初始触发功能。我将不胜感激。
解决方案
您的代码中存在明显的问题和反模式。cookieAvailable
将返回一个 Promise,因此您的支票if(!!cookieAvailable()) {
将始终是真实的。在检查是否确实有可用的 cookie 之前,您需要等待该 Promise 解决。
但实际上,您的cookieAvailable
函数会返回一个 Promise 包装器:如果thisChatClient.cookie.getCookieData
确实返回一个 Promise,则直接返回它,无需将其包装在 Promise 中。
如果它返回一个同步结果,那么你只会通过将它包装在 Promise 中来放松
async function requestCommunication(topic, customerId) {
function cookieAvailable() {
// this is already a Promise
return thisChatClient.cookie.getCookieData('sess_au');
}
const isCookieAvailable = await cookieAvailable();
if (!!isCookieAvailable) {
}
}
requestCommunication().catch(console.error);
现在所有这些都无助于对您的问题做出正确的回答:您的两个代码块之间的链接根本不清楚。
什么都不调用函数。
您getCookieData
将等待 MessageEvent,而不会让任何人知道它正在等待它。
我不确定您是如何计划让您的 iframe 知道它应该向您的窗口发送包含此信息的消息,但这是您必须考虑的事情。
但在去那里之前,我应该注意:尽管它可能很诱人,但在 Promises 中包装事件通常是一个坏主意。
事件和承诺是不同的东西,后者应该只解决一次,而前者可能会触发多次,并且来自不同的来源。
IMM 只有在您确定事件只会触发一次时才这样做是好的。使用 MessageEvent,您还远远不知道它。
您的用户很可能在他们的浏览器上有一个扩展,它将使用 postMessage 作为通信手段。如果在所有 iframe 上都添加了此扩展程序,则您的代码已损坏。
相反,您应该检查MessageChannel API,它将为您提供通信手段,您可以确定您将是唯一使用的人。
我不认为这个答案是解释这个 API 如何工作的正确地方,但是看看这个解释了非常基础的概述。
由于您一定会控制两端,因此您可以从那里建立一个基于 Promise 的系统。
在您的主页上,您将准备 MessageChannel 对象,并在侦听响应时将其发送到 iframe。当此响应出现时,您将能够解决您的 Promise。
在 iframe 中,您将在窗口上添加一个侦听器以捕获 MessageChannelPort。发生这种情况时,您将向您的服务请求 cookie 并通过 MessageChannel 的端口将其发送回。
即使在此交换期间您的主窗口出现一条消息,您也可以确定它不会是您等待的消息。
// Sets up a new MessageChannel
// so we can return a Promise
function getCookieData() {
return new Promise((resolve) => {
const channel = new MessageChannel();
// this will fire when iframe will answer
channel.port1.onmessage = e => resolve(e.data);
// let iframe know we're expecting an answer
// send it its own port
frame.contentWindow.postMessage('getCookie', '*', [channel.port2]);
});
}
frame.onload = async e => {
const frameHasCookie = await getCookieData();
console.log(frameHasCookie);
};
frame.src = generateFrameSRC();
function generateFrameSRC() {
// The content of your iframe
const cont = `
<html>
<head>
<script>
const originClean = "null";
onmessage = async e => {
// only if it's the origin we expected
// and if it does send a MessagePort
// and the message is "getCookie"
if(e.origin === originClean && e.ports && e.data === "getCookie") {
const data = await asyncData();
// respond to main window
e.ports[0].postMessage(data);
}
};
function asyncData() {
return new Promise(resolve =>
setTimeout(() => resolve("the data"), 1000)
);
}
<\/script>
</head>
<body>
hello
</body>
</html>`;
return 'data:text/html,' + encodeURIComponent(cont)
}
<iframe id="frame"></iframe>
推荐阅读
- excel - 如何显示错误信息和停止宏
- java - java jackson XML - 解析时如何忽略外包装?
- javascript - 在每秒的确切开始处(或每秒特定的毫秒数)运行一个函数
- java - Cucumber - 如何构建你的测试步骤?
- c# - 如何实现全局 WinAPI 覆盖接口
- html - 使用 Doxygen 内联显示图像
- visual-studio-code - 当窗格中的最后一个文件关闭时,防止 Visual Studio Code 更改窗口布局?
- php - php,如何作为类连接数据库,发送sql语句并返回结果
- node.js - 如何用 Sequelize 建立两个不直接相互依赖的表之间的关系?
- react-native - 如何在尴尬的 createStackNavigator 屏幕上居中标题文本?