上一篇中介紹了使用react-native呼叫ios native方法,在真實的使用場景中,不僅僅只是呼叫下Native的方法,還需要對結果進行處理,Native處理完之後返回結果再回撥會JavaScript中進行操作和處理。
這樣就需要使用JavaSctipt的回撥函式,對結果進行處理。在React Native中Object-c有兩種方式的回撥:RCTReponseSenderBlock
和Promises
。
RCTReponseSenderBlock
在JavaScript和Object-C的引數列表,有一類引數叫做RCTReponseSenderBlock
對於JavaScript的Function,這個就是JavaScript呼叫Object-C的Callback(回撥函式)。
RCTReponseSenderBlock
是在RCTBridgeModule.h
定義的block.
完整的定義:
typedef void (^RCTResponseSenderBlock)(NSArray *response);
RCTReponseSenderBlock
定義個一個Object-C Bridge的操作,返回給JavaScript一個callback的方法。
他的引數是一個NSArray
。其中第一個引數是error
代表著錯誤資訊,如果沒有錯誤傳入null,後面的引數傳入自定義的內容。
具體例項:
先給UIAlertView
新增兩個按鈕,在點選按鈕之後使用RCTResponseSenderBlock
返回JavaScript。
先實現UIAlertViewDelegate
:
@interface RNIOSAlert : NSObject<RCTBridgeModule,UIAlertViewDelegate>
@end
定義一個變數用來儲存引數:
@implementation RNIOSAlert{
RCTResponseSenderBlock _alertCallback;
}
@end
在clickedButtonAtIndex
方法中處理結果並呼叫回撥函式:
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (buttonIndex==0) {
_alertCallback(@[@"cancel",]);
}else{
_alertCallback(@[[NSNull null],@1]);
}
}
最後定義帶有回撥引數的Native方法:
RCT_EXPORT_METHOD(showAlertAndCallback:(RCTResponseSenderBlock)callback){
_alertCallback=callback;
UIAlertView * alertView=[[UIAlertView alloc] initWithTitle:@"react-native" message:@"是否繼續?" delegate:self cancelButtonTitle:@"關閉" otherButtonTitles:@"繼續", nil];
[alertView show];
}
最在JavaScript中呼叫Native方法,並處理回撥:
_alertCallback() {
RNIOSAlert.showAlertAndCallback(function (err, datas) {
if (err) {
console.warn(`err`, `已取消`);
} else {
console.warn(`data`, `請繼續`);
}
});
}
每次關閉UIAlertView
都可以看到JavaScript處理的結果。
Promises
Promises
是ES6
中的特性,它的目的是統一為JavaScript提供非同步程式設計的介面,避免Callback地獄,解決了Callback的層層巢狀。更加容易的對非同步操作進行控制。
在React Native中對Promises
有很完善的支援,呼叫Object-C 的Native方法的時候,也可以Promise的方式讓程式碼執行從Object-C 回到JavaScript中。
先看Promises的兩個狀態。
Resolve和Reject
Resolve
和Reject
分量是Promise的兩種狀態,表示已解決和已拒絕,Resolve
是正常的執行結果,而Reject
會觸發catch
操作。
在Object-C與之相對應的是:RCTPromiseResolveBlock
和RCTPromiseRejectBlock
,兩個都是定義好的Object-C bridge。
RCTPromiseResolveBlock
的實現:
typedef void (^RCTPromiseResolveBlock)(id result);
以id
為引數,當然傳參必須是Object-c和JavaScript定義好的引數。RCTPromiseRejectBlock
的實現:
typedef void (^RCTPromiseRejectBlock)(NSString *code, NSString *message, NSError *error);
RCTPromiseRejectBlock
使用了NSError,還可以傳入自定義的code以及message。
-
使用Promise實現回撥
首先要定義兩個變數用來儲存引數:
@implementation RNIOSAlert{
RCTPromiseResolveBlock _resolveBlock;
RCTPromiseRejectBlock _rejectBlock;
}
在定義提供給JavaScript呼叫的Native函式:
RCT_REMAP_METHOD(alertUserPromise, resolver:(RCTPromiseResolveBlock)resolver rejecter:(RCTPromiseRejectBlock)reject){
_resolveBlock=resolver;
_rejectBlock=reject;
UIAlertView * alertView=[[UIAlertView alloc] initWithTitle:@"react-native" message:@"使用Promise?" delegate:self cancelButtonTitle:@"關閉" otherButtonTitles:@"繼續", nil];
[alertView show];
}
這裡使用RCT_REMAP_METHOD
巨集定義Native,他的第一個引數是方法名,後面的引數是方法的實現,在JavaScript呼叫Promise時並不需要在方法名中體現。
處理結果並使用回撥:
-(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(@[@1]);
}
}
JavaScript中呼叫該方法:
_alertUsePromise() {
RNIOSAlert.alertUserPromise().then((datas)=> {
console.warn(`data`, datas);
}).catch((err)=> {
console.warn(`err`, err);
});
}
這裡的then處理的是Resovle
狀態的結果,而catch處理的是Reject
狀態的結果。
也可以使用async/await實現
async _alertPromise() {
try {
var datas = await RNIOSAlert.alertUserPromise();
console.warn(`data`, datas);
} catch (e) {
console.warn(`err`, e);
}
}
async/await
是兩個關鍵詞,用來把Promises
的思想融入到語言本身。使用他們就不再需要寫catch這樣的偽同步的程式碼,直接使用try/catch/return這樣的關鍵詞就可以了.
Promises對於回撥的使用很便利,儘量避免了JavaScript的回撥地獄。