You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 22:21:02