React Native iOS模拟器重载后async函数await卡住问题求助
问题原因分析
- 远程JS调试模式异常:iOS模拟器开启Remote JS Debugging时,JS代码运行在Chrome V8引擎而非原生JS运行时,热重载后调试器与RN原生侧的事件循环同步失效,
setTimeout等宏任务的回调无法被正常触发,导致Promise永久处于pending状态,await逻辑因此卡住。 - 旧版本Hermes引擎Bug:React Native 0.66~0.69的部分版本搭载的Hermes引擎存在已知问题,热重载后Promise调度队列被阻塞,宏任务回调无法被正常消费。
- 组件上下文销毁:如果
processPayment函数定义在组件内部,热重载后旧组件实例被销毁,绑定到旧实例的setTimeout回调的上下文失效,resolve方法无法正常执行。
排查步骤
- 优先关闭远程JS调试验证:点击模拟器按
⌘+D打开开发者菜单,选择「Stop Debugging」,重新加载JS bundle后测试重载后功能是否恢复。 - 替换宏任务实现验证:将
setTimeout替换为React Native提供的InteractionManager.runAfterInteractions测试,确认是否为宏任务调度层面的问题:
const data = await new Promise((resolve) => { InteractionManager.runAfterInteractions(() => resolve({ item: 10 })) })
- 验证Hermes引擎影响:临时关闭Hermes后重新打包测试,在ios/Podfile中修改
hermes_enabled => false,执行pod install后重新构建应用,确认问题是否消失。
解决方法
- 日常开发无需调试时优先关闭远程JS调试,该问题仅出现在调试模式下,不会影响Release包的正常运行。
- 如果是Hermes引擎版本问题,升级React Native到0.70及以上版本,该版本已修复热重载后Promise调度异常的问题。
- 调试过程中遇到该问题时,可按
⌘+R全量刷新JS bundle即可恢复,无需删除应用重新构建。 - 若
processPayment不依赖组件内部状态,可将其移到组件定义外部,避免热重载时组件重渲染导致的函数上下文重建问题。 - 组件内部的异步逻辑可添加取消标记,避免热重载后旧实例的Promise泄漏:
useEffect(() => { let isCancelled = false const processPayment = async (amount: number) => { console.log("start fetching") const data = await new Promise((resolve) => { setTimeout(() => { if (!isCancelled) resolve({ item: 10 }) }, 1000) }) if (!isCancelled) { console.log(data) console.log("end fetching") } } return () => { isCancelled = true } }, [])
内容的提问来源于stack exchange,提问作者DanielNguyen
相关产品推荐
相关产品推荐

