Promise内window.onstorage仅触发一次:能否在Promise中监听事件?
问题解答
可以在Promise相关逻辑中监听storage事件,但你的写法存在两个核心问题,导致只能监听到一次事件:
当前写法的问题
- Promise的决议特性限制:Promise一旦执行
resolve或reject,就会进入已完成/已拒绝的终态,后续再调用resolve不会有任何效果。你把resolve放在window.onstorage回调里,第一次触发storage事件时Promise就完成了,后续事件触发时回调里的resolve已经失效。 - 事件注册方式错误:使用
window.onstorage = ...是直接覆盖事件处理函数,而非添加新的监听器。这种写法不仅容易覆盖其他地方的事件逻辑,而且结合Promise的特性,直接导致后续事件无法被捕获。
正确实现方式
如果需要持续监听storage变化,不能把事件回调和单个Promise的resolve绑定。应该把初始数据获取(用Promise)和持续事件监听分开处理,同时使用标准的事件注册方式addEventListener,并在组件卸载时清理监听器避免内存泄漏。
修改后的代码示例:
const connect = useCallback(() => { // 用Promise处理初始数据的异步获取 return Promise.all([infuraStorageData, metamaskStorageData]) .then(response => { const initialData = { infuraData: response[0], metamaskData: response[1] }; // 返回初始数据,以及用于订阅storage变化的方法 return { initialData, subscribe(onStorageChange) { const storageHandler = (e) => { // 这里可以根据事件e获取最新的storage数据,或者重新请求 Promise.all([infuraStorageData, metamaskStorageData]) .then(updatedResponse => { onStorageChange({ infuraData: updatedResponse[0], metamaskData: updatedResponse[1] }); }); }; window.addEventListener('storage', storageHandler); // 返回取消订阅的函数,用于组件卸载时清理 return () => window.removeEventListener('storage', storageHandler); } }; }) .catch(error => Promise.reject(error)); }, [infuraStorageData, metamaskStorageData]); useEffect(() => { let unsubscribe; useStorageDBHook.connect() .then(({ initialData, subscribe }) => { console.log('初始连接数据:', initialData); // 订阅storage变化,每次触发都会执行回调 unsubscribe = subscribe((updatedData) => { console.log('storage更新:', updatedData); }); }) .catch(e => console.error('连接失败:', e)); // 组件卸载时取消监听,避免内存泄漏 return () => { if (unsubscribe) unsubscribe(); }; }, [useStorageDBHook]);
关键改进点
- 把初始数据获取和事件监听拆解开:Promise只负责完成初始的异步数据加载,事件监听通过独立的
subscribe方法处理。 - 使用
addEventListener注册事件,支持多个监听器,且能通过removeEventListener清理。 - 组件卸载时主动移除事件监听器,避免内存泄漏。
- 每次storage事件触发时,重新获取最新数据并通过回调通知,保证响应所有变化。
内容的提问来源于stack exchange,提问作者THIAGO DE BONIS
相关产品推荐
相关产品推荐

