如何避免 addEventListener 和 window.open 之间的竞争条件
How to avoid race condition between `addEventListener` and `window.open`
作为我网站 OAuth 流程的一部分,我使用 window.open
允许用户登录到第 3 方提供商,并使用 postMessage
将身份验证令牌发送回我的网页。基本流程看起来像这样,尽管我删除了与超时、清理、错误处理等相关的额外代码:
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 的页面,该页面仅调用 window.opener.postMessage(...)
现在,我的问题的症结在于这里实际上存在竞争条件,至少在理论上是这样。问题是 childWindow 可以假设 调用 postMessage before addEventListener
被调用。相反,如果我先调用 addEventListener
,它可能会在 before 设置 childWindow
之前收到消息,我 认为 会抛出和例外?关键问题似乎是 window.open
不是异步的,所以我无法自动生成 window 和 设置消息处理程序。
目前,我的解决方案是先设置事件处理程序,并假设在执行该函数时消息不会进来。这是一个合理的假设吗?如果不是,是否有更好的方法来确保此流程正确?
这里不应该存在竞争条件,因为window.open
和window.addEventListener
在同一个tick中同时出现,而Javascript既是单线程又是不可重入的.
如果子 window 确实设法在对 window.open
的调用完成之前发布消息,则该消息将简单地进入当前 Javascript 上下文的事件队列。您的代码仍然是 运行,因此接下来会调用 addEventListener
。最后,在我们在这里看不到的某个未来时间点,您的代码 returns 控制浏览器,结束当前滴答。
一旦当前滴答结束,浏览器就可以检查事件队列并调度下一个工作(大概是消息)。您的订阅者已经到位,所以一切都很好!
作为我网站 OAuth 流程的一部分,我使用 window.open
允许用户登录到第 3 方提供商,并使用 postMessage
将身份验证令牌发送回我的网页。基本流程看起来像这样,尽管我删除了与超时、清理、错误处理等相关的额外代码:
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 的页面,该页面仅调用 window.opener.postMessage(...)
现在,我的问题的症结在于这里实际上存在竞争条件,至少在理论上是这样。问题是 childWindow 可以假设 调用 postMessage before addEventListener
被调用。相反,如果我先调用 addEventListener
,它可能会在 before 设置 childWindow
之前收到消息,我 认为 会抛出和例外?关键问题似乎是 window.open
不是异步的,所以我无法自动生成 window 和 设置消息处理程序。
目前,我的解决方案是先设置事件处理程序,并假设在执行该函数时消息不会进来。这是一个合理的假设吗?如果不是,是否有更好的方法来确保此流程正确?
这里不应该存在竞争条件,因为window.open
和window.addEventListener
在同一个tick中同时出现,而Javascript既是单线程又是不可重入的.
如果子 window 确实设法在对 window.open
的调用完成之前发布消息,则该消息将简单地进入当前 Javascript 上下文的事件队列。您的代码仍然是 运行,因此接下来会调用 addEventListener
。最后,在我们在这里看不到的某个未来时间点,您的代码 returns 控制浏览器,结束当前滴答。
一旦当前滴答结束,浏览器就可以检查事件队列并调度下一个工作(大概是消息)。您的订阅者已经到位,所以一切都很好!