React-Native(iOS)中如何利用代理方法返回结果触发回调?
解决方案:绑定异步代理结果与RN回调/Promise
你的核心问题在于:调用SDK方法后立刻执行回调,但代理方法是异步返回结果的,导致回调拿到的是旧值或未赋值的初始值;另外RCTEventEmitter的不稳定源于未正确匹配请求与事件触发。以下是两种可靠的实现方式:
一、回调绑定方案(解决结果滞后问题)
实现思路
- 不再调用SDK后立即执行回调,而是将回调暂存,等待代理方法触发时再执行对应的回调
- 为播放/停止操作分别维护独立的回调变量,避免操作混淆
- 代理方法执行完回调后立即置空,防止重复触发
代码实现
1. 模块属性声明与初始化
@interface YourRCTModule () <CameraSirenDelegate> @property (nonatomic, strong) CameraControl *cameraControl; // 暂存播放/停止操作的回调 @property (nonatomic, copy) RCTResponseSenderBlock pendingPlayCallback; @property (nonatomic, copy) RCTResponseSenderBlock pendingStopCallback; @end @implementation YourRCTModule - (instancetype)init { self = [super init]; if (self) { self.cameraControl = [[CameraControl alloc] init]; // 仅初始化时设置一次代理,无需每次调用方法重复设置 self.cameraControl.cameraSiren.delegate = self; } return self; }
2. 修改RN导出方法
RCT_EXPORT_METHOD(openSirenAlarm: (RCTResponseSenderBlock) callback) { // 检查是否有未完成的播放请求,避免重复操作 if (self.pendingPlayCallback) { callback(@[@(1), @"警报正在启动中"]); return; } // 暂存回调,等待代理结果返回 self.pendingPlayCallback = callback; [self.cameraControl.cameraSiren playCameraSiren]; } RCT_EXPORT_METHOD(closeSirenAlarm: (RCTResponseSenderBlock) callback) { if (self.pendingStopCallback) { callback(@[@(1), @"警报正在停止中"]); return; } self.pendingStopCallback = callback; [self.cameraControl.cameraSiren stopCameraSiren]; }
3. 代理方法中执行回调
#pragma mark CameraSirenDelegate - (void)cameraSiren:(CameraSiren *)cameraSiren stopResult:(ENUM_APP_RESULT)result { NSLog(@"stop result: %u", result); NSInteger errorCode = (result == RESULT_SUCCESS) ? 0 : 1; NSInteger responseVal = (result == RESULT_SUCCESS) ? 1 : 0; // 执行暂存的停止回调并置空 if (self.pendingStopCallback) { self.pendingStopCallback(@[@(errorCode), @(responseVal)]); self.pendingStopCallback = nil; } } - (void)cameraSiren:(CameraSiren *)cameraSiren playResult:(ENUM_APP_RESULT)result { NSLog(@"start result: %u", result); NSInteger errorCode = (result == RESULT_SUCCESS) ? 0 : 1; NSInteger responseVal = (result == RESULT_SUCCESS) ? 1 : 0; // 执行暂存的播放回调并置空 if (self.pendingPlayCallback) { self.pendingPlayCallback(@[@(errorCode), @(responseVal)]); self.pendingPlayCallback = nil; } }
二、Promise方案(更优雅的异步处理)
如果JS端更倾向于async/await语法,可以将导出方法改为Promise类型,实现逻辑与回调绑定一致,但代码更简洁:
代码实现
// 替换原有的RCT_EXPORT_METHOD为RCT_REMAP_METHOD RCT_REMAP_METHOD(openSirenAlarm, resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { if (self.pendingPlayCallback) { reject(@"ALREADY_STARTING", @"警报正在启动中", nil); return; } // 将Promise的resolve/reject封装为回调暂存 self.pendingPlayCallback = ^(NSArray *results) { NSNumber *errorNum = results[0]; NSNumber *responseNum = results[1]; if ([errorNum integerValue] == 0) { resolve(responseNum); } else { reject(@"START_FAILED", @"启动警报失败", nil); } }; [self.cameraControl.cameraSiren playCameraSiren]; } RCT_REMAP_METHOD(closeSirenAlarm, resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { if (self.pendingStopCallback) { reject(@"ALREADY_STOPPING", @"警报正在停止中", nil); return; } self.pendingStopCallback = ^(NSArray *results) { NSNumber *errorNum = results[0]; NSNumber *responseNum = results[1]; if ([errorNum integerValue] == 0) { resolve(responseNum); } else { reject(@"STOP_FAILED", @"停止警报失败", nil); } }; [self.cameraControl.cameraSiren stopCameraSiren]; }
JS端调用示例
try { const response = await openSirenAlarm(); console.log('警报启动成功:', response); } catch (error) { console.error('警报启动失败:', error); }
关于RCTEventEmitter的问题
如果仍想使用事件发射器,需确保:
- 仅在代理方法触发时单次emit事件,并在事件数据中携带操作类型(播放/停止)
- 正确实现
startObserving和stopObserving方法,管理订阅生命周期 - 避免重复设置代理或多次注册事件监听
但对于这种单次请求-响应的场景,回调/Promise方案更直接,能精准匹配每个操作的结果,避免事件触发混乱的问题。
内容的提问来源于stack exchange,提问作者Rej Fredericks
相关产品推荐
相关产品推荐

