最近有个提供给RN的库需要提供同步的方法,我想当然的以为用callback的方式,在当前线程同步调用原生逻辑之后,将结果callback回去就可以实现了,结果实际调试起来发现是想当然了
原生提供给JS调用并返回数据的几种方式
Promise
RCT_EXPORT_METHOD(promiseGetItem:(NSString *)key resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
// do sth
if (resolve) {
resolve(@"222");
}
}
Callback
RCT_EXPORT_METHOD(callbackGetItem:(NSString *)key callback:(RCTResponseSenderBlock)callback) {
// do sth
if (callback) {
callback(@[@"222"]);
}
}
BLOCKING_SYNCHRONOUS
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(syncGetItem:(NSString *)key) {
// do sth
return @"222";
}
Android的同步方式也贴一下代码
@ReactMethod(isBlockingSynchronousMethod = true)
public String syncGetItem(String key) {
return "222";
}
直接上结论,BLOCKING_SYNCHRONOUS
的方式是可以做到同步的,另外的方式程序执行不会等结果返回,会继续执行后面的逻辑,不符合预期。
分析下为什么
为什么了?看到是一堆的宏,我们将其展开
图片.png
展开之后的代码片段
@implementation TestModule
extern __attribute__((visibility("default"))) void RCTRegisterModule(Class); + (NSString *)moduleName { return @""; } + (void)load { RCTRegisterModule(self); }
+ (const RCTMethodInfo *)__rct_export__140 { static RCTMethodInfo config = {"", "promiseGetItem:(NSString *)key resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject", __objc_no}; return &config; } - (void)promiseGetItem:(NSString *)key resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject ; {
if (resolve) {
resolve(@"222");
}
}
+ (const RCTMethodInfo *)__rct_export__211 { static RCTMethodInfo config = {"", "callbackGetItem:(NSString *)key callback:(RCTResponseSenderBlock)callback", __objc_no}; return &config; } - (void)callbackGetItem:(NSString *)key callback:(RCTResponseSenderBlock)callback ; {
if (callback) {
callback(@[@"222"]);
}
}
+ (const RCTMethodInfo *)__rct_export__282 { static RCTMethodInfo config = {"", "syncGetItem:(NSString *)key", __objc_yes}; return &config; } - (id)syncGetItem:(NSString *)key ; {
return @"222";
}
@end
观察差别,是config的最后一个参数BLOCKING_SYNCHRONOUS
方式传的是__objc_yes
其他的传的是__objc_no
,我们看下config的定义
typedef struct RCTMethodInfo {
const char *const jsName;
const char *const objcName;
const BOOL isSync;
} RCTMethodInfo;
这里就很明显了,最后那个参数就是标记方法是同步还是异步的了;
我们再看它是怎么做到的,我截取了js调用原生方法的一个入口函数的部分代码,方法的具体实现在RCTModuleMethod.mm
文件中
- (id)invokeWithBridge:(RCTBridge *)bridge
module:(id)module
arguments:(NSArray *)arguments {
// 此处省略很多代码
[_invocation invokeWithTarget:module];
if (_methodInfo->isSync) {
void *returnValue;
[_invocation getReturnValue:&returnValue];
return (__bridge id)returnValue;
}
return nil;
}
可以看到,当方法是isSync的时候,调用之后,就获取返回值,并返回回去了,否则就直接返回nil,那么像Promise、Callback的方式结果就不是立马返回的,而是最终等到原生执行逻辑之后手动触发回掉之后返回。
网友评论