如何立即监听Firebase Auth Provider错误事件?弹窗关闭延迟问题
关于Firebase Auth signInWithPopup弹窗关闭延迟的问题
当使用Firebase Auth Provider的signInWithPopup方法时,用户点击关闭弹窗会返回error.code === 'auth/popup-closed-by-user',但存在一定延迟。请问该延迟产生的原因是什么?如何实现弹窗关闭时立即监听该错误事件?
以下是原使用signInWithPopup的代码:
export const signInWithSocial = async (type: string) => { let provider: any; if (type === SocialLoginType.google) { provider = googleProvider; } else if (type === SocialLoginType.facebook) { provider = facebookProvider; provider.setCustomParameters({ display: 'popup' }); } else if (type === SocialLoginType.twitter) { provider = twitterProvider; provider.setCustomParameters({ lang: 'en' }); } else if (type === SocialLoginType.apple) { provider = appleProvider; provider.addScope('email'); provider.addScope('name'); } auth.signInWithPopup(provider) .then(async (result) => { const { user } = result; const idToken = await user.getIdToken(); window.localStorage.setItem('idToken', idToken); window.dispatchEvent(new Event('storage')); }) .catch((error) => { console.log('--- Login Error: ' + JSON.stringify(error) + ' ---\n'); const customEvent = new CustomEvent('stopSocialProcess'); window.dispatchEvent(customEvent); }); };
延迟产生的原因
- Firebase内部轮询机制:
signInWithPopup底层通过固定间隔的轮询检测弹窗状态,而非实时监听关闭事件,轮询间隔直接导致了延迟。 - 第三方提供商回调链路:关闭弹窗后,Firebase需要等待第三方身份提供商的回调确认,部分提供商的响应流程较长,会额外增加延迟。
- 浏览器跨窗口通信限制:不同浏览器对跨窗口消息的处理逻辑有差异,部分场景下消息传递会出现排队等待,进一步拉长延迟时间。
实现实时监听弹窗关闭的方案
放弃依赖signInWithPopup的catch回调,改为手动创建弹窗并监听关闭事件,配合signInWithRedirect完成登录流程,代码示例如下:
export const signInWithSocial = async (type: string) => { let provider: any; // 配置弹窗尺寸与位置 const popupWidth = 500; const popupHeight = 600; const left = window.screen.width / 2 - popupWidth / 2; const top = window.screen.height / 2 - popupHeight / 2; const popupOptions = `width=${popupWidth},height=${popupHeight},left=${left},top=${top},resizable=yes`; // 初始化对应身份提供商 if (type === SocialLoginType.google) { provider = googleProvider; } else if (type === SocialLoginType.facebook) { provider = facebookProvider; provider.setCustomParameters({ display: 'popup' }); } else if (type === SocialLoginType.twitter) { provider = twitterProvider; provider.setCustomParameters({ lang: 'en' }); } else if (type === SocialLoginType.apple) { provider = appleProvider; provider.addScope('email'); provider.addScope('name'); } // 手动创建登录弹窗 const authPopup = window.open('', '_blank', popupOptions); if (!authPopup) { console.error('无法打开登录弹窗,请检查浏览器弹窗设置'); return; } // 短间隔轮询检测弹窗关闭状态 const popupCheckInterval = setInterval(() => { if (authPopup.closed) { clearInterval(popupCheckInterval); // 立即触发自定义停止事件 const customEvent = new CustomEvent('stopSocialProcess'); window.dispatchEvent(customEvent); // 清理Firebase未完成的登录流程 auth.signOut().catch(() => {}); } }, 100); // 100ms间隔接近实时响应 try { // 使用redirect方式触发登录流程 await auth.signInWithRedirect(provider); } catch (error) { clearInterval(popupCheckInterval); console.log('--- Login Error: ' + JSON.stringify(error) + ' ---\n'); const customEvent = new CustomEvent('stopSocialProcess'); window.dispatchEvent(customEvent); } }; // 在页面初始化时处理redirect登录结果 auth.getRedirectResult() .then(async (result) => { if (result?.user) { const idToken = await result.user.getIdToken(); window.localStorage.setItem('idToken', idToken); window.dispatchEvent(new Event('storage')); } }) .catch((error) => { console.log('--- Redirect Result Error: ' + JSON.stringify(error) + ' ---\n'); });
方案说明
- 手动创建弹窗后,通过100ms间隔的轮询检测弹窗状态,比Firebase内部的轮询更及时;
- 配合
signInWithRedirect和getRedirectResult完成完整登录流程,避免依赖signInWithPopup的延迟回调; - 弹窗关闭时立即触发自定义事件,实现实时响应需求。
内容的提问来源于stack exchange,提问作者Youth Dream
相关产品推荐
相关产品推荐

