作为我网站的OAuth流程的一部分,我正在使用window.open
允许用户登录到第三方提供商,并使用postMessage
将auth令牌发送回我的网页。基本流程看起来像这样,尽管我删除了与超时,清理,错误处理等有关的额外代码:
const getToken = () => new Promise((resolve, reject) => {
const childWindow = window.open(buildUrl({
url: "https://third-party-provider.com/oauth/authorize",
query: {
redirect_uri: "my-site.com/oauth-callback"
}
})
window.addEventListener('message', event => {
if(
event.origin === window.location.origin &&
event.source === childWindow
) {
if(event.data.error) reject(event.data.error)
else resolve(event.data)
}
})
// Plus some extra code to remove the event listener and close
// the child window.
}
基本思想是,当用户单击授权时,它们将被重定向到my-site.com/oauth-callback
。该页面完成了服务器端的OAuth流程,然后加载了包含少量javascript的页面,这些javascript简单地调用了window.opener.postMessage(...)
现在,我的问题的症结在于,至少在理论上,这里确实存在种族问题。问题是childWindow可以在调用addEventListener
之前假设地调用postMessage 。相反,如果我先呼叫addEventListener
,它可能会在设置了{em> childWindow
之前收到消息,我认为会抛出该异常?关键问题似乎是window.open
不是异步的,所以我不能自动生成窗口 并设置消息处理程序。
当前,我的解决方案是先设置事件处理程序,并假定在执行该函数时消息没有进入。这是一个合理的假设吗?如果不是,是否有更好的方法来确保该流程正确?
答案 0 :(得分:1)
这里不应该存在争用条件,因为window.open
和window.addEventListener
一起出现在同一滴答中,并且Javascript既是单线程的又是不可重入的。
如果子窗口确实在对window.open
的调用完成之前成功发布了一条消息,则该消息将直接进入当前Javascript上下文的事件队列。您的代码仍在运行,因此接下来发生addEventListener
调用。最后,在我们这里看不到的将来,您的代码将控制权返回给浏览器,从而结束了当前的滴答声。
当前滴答结束后,浏览器可以检出事件队列并调度下一部分工作(可能是消息)。您的订户已经就位,所以一切都很好!