Expo v42 iOS端expo-notifications异步调用冲突报错求助
问题根因
- 报错本质是
Notifications.getExpoPushTokenAsync()方法被重复触发:前一次调用的异步任务还未执行完成,第二次调用就已发起,你使用的Expo SDK 42版本的expo-notifications库对该方法默认增加了并发调用限制,避免底层APNS接口重复请求产生异常,因此会直接抛出冲突错误。 - 常见的重复触发场景包括:组件多次挂载/重新渲染时重复调用注册函数、App前后台切换时重复触发注册逻辑、iOS端权限弹窗弹出到用户点击确认的间隙,有其他逻辑再次触发了推送注册流程。
- 你当前的代码未做重复调用拦截,Debug模式下JS执行速度相对慢不容易触发冲突,但Release打包后JS执行效率提升,并发冲突概率大幅上升,所以仅在iOS打包版稳定复现。
解决方案
1. 增加全局互斥锁防止重复调用
在全局作用域声明状态变量,标记当前是否正在执行推送注册流程,调用前先判断状态,执行完成后无论成功失败都重置状态:
// 全局添加锁标记 let isRegisteringPush = false async function registerForPushNotificationsAsync() { // 已经在注册中直接返回,避免重复调用 if (isRegisteringPush) return isRegisteringPush = true let token try { if (Constants.isDevice) { const { status: existingStatus } = await Notifications.getPermissionsAsync() let finalStatus = existingStatus if (existingStatus !== 'granted') { const { status } = await Notifications.requestPermissionsAsync({ ios: { allowAlert: true, allowBadge: true, allowSound: true, allowAnnouncements: true, }, }) finalStatus = status } if (finalStatus !== 'granted') { console.log('noti cancel') return } const res = await Notifications.getExpoPushTokenAsync() console.log('getExpoPUshTokenAsync >> ', res) token = res.data } else { Alert.alert('Must use physical device for Push Notifications') } if (Platform.OS === 'android') { await Notifications.setNotificationChannelAsync('default', { name: 'default', importance: Notifications.AndroidImportance.MAX, vibrationPattern: [0, 250, 250, 250], lightColor: '#FF231F7C', }) } } catch (err) { console.log('push register error >> ', err) // 可选:匹配到重复调用错误时延迟重试一次 if (err.message.includes('Another async call to this method is in progress')) { setTimeout(() => { isRegisteringPush = false registerForPushNotificationsAsync() }, 300) } } finally { // 不管执行成功还是失败都重置锁 isRegisteringPush = false } return token }
2. 优化调用时机
- 不要在组件的
useEffect无依赖或者短周期依赖中调用该注册函数,建议仅在App启动完成、用户主动触发权限申请、或者检测到推送令牌过期时才调用一次。 - React框架下可以把注册逻辑放在根组件的
useEffect空依赖数组中执行,保证整个App生命周期只执行一次初始化注册。
内容的提问来源于stack exchange,提问作者Dev_Kim
相关产品推荐
相关产品推荐

