( Native 方法理解為 iOS/Android 原生代碼 )
在真實的使用場景中,不僅僅只是調(diào)用下Native的方法,還需要對結(jié)果進(jìn)行處理,Native處理完之后返回結(jié)果再回調(diào)會JavaScript中進(jìn)行操作和處理。
這樣就需要使用JavaSctipt的回調(diào)函數(shù),對結(jié)果進(jìn)行處理。在React Native中Object-c有兩種方式的回調(diào):RCTReponseSenderBlock
和Promises
。
1.RCTReponseSenderBlock
在JavaScript和Object-C的參數(shù)列表,有一類參數(shù)叫做RCTReponseSenderBlock
對應(yīng)JavaScript的Function,這個就是JavaScript調(diào)用Object-C的Callback(回調(diào)函數(shù))。
RCTReponseSenderBlock
是在RCTBridgeModule.h
定義的block.
完整的定義:typedef void (^RCTResponseSenderBlock)(NSArray *response);
RCTReponseSenderBlock
定義個一個Object-C Bridge的操作,返回給JavaScript一個callback的方法。
他的參數(shù)是一個NSArray
。其中第一個參數(shù)是error
代表著錯誤信息,如果沒有錯誤傳入null
,后面的參數(shù)傳入自定義的內(nèi)容。
具體實例:
先給UIAlertView
添加兩個按鈕,在點擊按鈕之后使用RCTResponseSenderBlock
返回JavaScript。
先實現(xiàn)UIAlertViewDelegate
:
@interface RNIOSAlert : NSObject<RCTBridgeModule,UIAlertViewDelegate>
@end
定義一個變量用來保存參數(shù):
@implementation RNIOSAlert{
RCTResponseSenderBlock _alertCallback;
}
@end
在clickedButtonAtIndex方法中處理結(jié)果并調(diào)用回調(diào)函數(shù):
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
switch (alertView.tag) {
case 0:
if (buttonIndex == 0) {
_alertCallback(@[@"取消"]);
}else{
_alertCallback(@[[NSNull null],@"確定"]);
}
break;
default:
break;
}
}
最后定義帶有回調(diào)參數(shù)的Native方法:(把 alertView 放在主線程中顯示,否則會卡頓,一段時間后才能顯示出)
RCT_EXPORT_METHOD(showAlertViewAndMsg:(NSString *)message Callback:(RCTResponseSenderBlock) alertCallback){
_alertCallback = alertCallback;
dispatch_sync(dispatch_get_main_queue(), ^{
UIAlertView * alert = [[UIAlertView alloc]initWithTitle:@"React Native" message:message delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"繼續(xù)", nil];
alert.tag = 0;
[alert show];
});
}
最在JavaScript中調(diào)用Native方法,并處理回調(diào):
_alertCallback() {
RNIOSAlert.showAlertAndCallback(function (err, datas) {
if (err) {
console.warn('err', '已取消');
} else {
console.warn('data', '請繼續(xù)');
}
});
}
每次關(guān)閉UIAlertView都可以看到JavaScript處理的結(jié)果。
2.Promises
Promises是ES6
中的特性,它的目的是統(tǒng)一為JavaScript提供異步編程的接口,避免Callback地獄,解決了Callback的層層嵌套。更加容易的對異步操作進(jìn)行控制。詳解參考:React Native - Promise 對象
在React Native中對Promises有很完善的支持,調(diào)用Object-C 的Native方法的時候,也可以Promise的方式讓代碼執(zhí)行從Object-C 回到JavaScript中。
先看Promises的兩個狀態(tài)。
Resolve和Reject是Promise的兩種狀態(tài),表示已解決和已拒絕,
- Resolve是已解決的執(zhí)行結(jié)果,會觸發(fā)then操作
- Reject是已拒絕的執(zhí)行結(jié)果,會觸發(fā)catch操作
在Object-C與之相對應(yīng)的是:RCTPromiseResolveBlock
和RCTPromiseRejectBlock
,兩個都是定義好的Object-C bridge。
RCTPromiseResolveBlock
的實現(xiàn):
typedef void (^RCTPromiseResolveBlock)(id result);
以id為參數(shù),當(dāng)然傳參必須是Object-c和JavaScript定義好的參數(shù)。
RCTPromiseRejectBlock
的實現(xiàn):
typedef void (^RCTPromiseRejectBlock)(NSString *code, NSString *message, NSError *error);
RCTPromiseRejectBlock使用了NSError,還可以傳入自定義的code以及message。
NSError * error =[NSError errorWithDomain:@"test" code:0 userInfo:nil];
使用Promise實現(xiàn)回調(diào)
首先要定義兩個變量用來保存參數(shù):
@implementation RNIOSAlert{
RCTPromiseResolveBlock _resolveBlock;
RCTPromiseRejectBlock _rejectBlock;
}
在定義提供給JavaScript調(diào)用的Native函數(shù):
RCT_REMAP_METHOD(alertUsePromise,resolve:(RCTPromiseResolveBlock)resolve
reject:(RCTPromiseRejectBlock)reject){
_resolveBlock = resolve;
_rejectBlock = reject;
dispatch_sync(dispatch_get_main_queue(), ^{
UIAlertView * alert = [[UIAlertView alloc]initWithTitle:@"React Native " message:@"使用Promise實現(xiàn)回調(diào) alertUsePromise" delegate:self cancelButtonTitle:@"取消" otherButtonTitles:@"繼續(xù)", nil];
alert.tag = 1;
[alert show];
});
}
這里使用RCT_REMAP_METHOD
宏定義Native,他的第一個參數(shù)是方法名,后面的參數(shù)是方法的實現(xiàn),在JavaScript調(diào)用Promise時并不需要在方法名中體現(xiàn)。
處理結(jié)果并使用回調(diào):
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex == 0) {
NSError * err=[NSError errorWithDomain:@"test" code:0 userInfo:nil];
_rejectBlock(@"0",@"cancel",err);
}else{
_resolveBlock(@[@"成功"]);
}
}
JavaScript中調(diào)用該方法:
_alertUsePromise() {
RNIOSAlert.alertUserPromise().then((datas)=> {
console.warn('data', datas);
}).catch((err)=> {
console.warn('err', err);
});
}
這里的then處理的是Resovle狀態(tài)的結(jié)果,而catch處理的是Reject狀態(tài)的結(jié)果。
也可以使用async/await實現(xiàn)
async _alertPromise() {
try {
var datas = await RNIOSAlert.alertUserPromise();
console.warn('data', datas);
} catch (e) {
console.warn('err', e);
}
}
async/await
是兩個關(guān)鍵詞,用來把Promises
的思想融入到語言本身。使用他們就不再需要寫catch這樣的偽同步的代碼,直接使用try/catch/return這樣的關(guān)鍵詞就可以了.
Promises對于回調(diào)的使用很便利,盡量避免了JavaScript的回調(diào)地獄。