Promise在web前端的開發工程師中使用的非常頻繁,通過then使同步的程式設計思路來進行非同步呼叫,使得非同步編碼變得非常的簡單。iOS也有PromiseKit這樣的開源庫可以使用,不過自己實現一個使用起來會更方便和貼近。
web端的Promise使用
//第一個非同步任務
function run_a(){
return new Promise(function(resolve, reject){
//假設已經進行了非同步操作,並且獲得了資料
resolve("step1");
});
}
//第二個非同步任務
function run_b(data_a){
return new Promise(function(resolve, reject){
//假設已經進行了非同步操作,並且獲得了資料
console.log(data_a);
resolve("step2");
});
}
//第三個非同步任務
function run_c(data_b){
return new Promise(function(resolve, reject){
//假設已經進行了非同步操作,並且獲得了資料
console.log(data_b);
resolve("step3");
});
}
//連續呼叫
run_a().then(function(data){
return run_b(data);
}).then(function(data){
return run_c(data);
}).then(function(data){
console.log(data);
});
/*執行結果
step1
step2
step3
*/
複製程式碼
參考上面的例子,對Promise稍微有些瞭解的應該都知道Promise包裝了一個非同步呼叫並生成一個Promise例項,當非同步呼叫返回的時候根據呼叫的結果分別呼叫例項化時傳入的resolve 和 reject方法,這個時候then就會接收到對應的資料,這個是Promise最基本使用,當然真正的Promise還包含了很多其它的使用,這裡我就使用OC實現最基本的非同步呼叫then功能。
Objective C 的實現
實現程式碼放在github上,只有一個類檔案可以隨意的copy到工程中:github.com/MathewWang/…
如何使用
// Resolve Example
WWPromise.promise(^(ResolveFunc resolve, RejectFunc reject){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
resolve(@"aaa");
});
}).then(^(NSString *result){
NSLog(@"###promise the result is 111: %@", result);
return WWPromise.promise(^(ResolveFunc resolve, RejectFunc reject){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
resolve([result stringByAppendingString:@"bbb"]);
});
});
}, nil).then(^(NSString *result){
NSLog(@"###promise the result is 222: %@", result);
return [WWPromise empty];
}, nil);
// Reject Example
WWPromise.promise(^(ResolveFunc resolve, RejectFunc reject){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
reject(@"aaa");
});
}).then(nil, ^(NSString *result){
NSLog(@"###promise the result is 111: %@", result);
return [WWPromise empty];
});
複製程式碼
參照上面的例子,在新增then的時候,對於resolve 和 reject的回撥函式,如果不需要直接傳入nil即可,不需要返回引數的then需要返回一個**[WWPromise empty]。 ###One More 因為Promise在例項化的時候就開始執行非同步呼叫了,這點和RAC的冷訊號不一樣(冷訊號是訂閱時觸發非同步呼叫的),因此我們可以將Promise**再進行一次封裝使得對於OC的開發更加友好,這也是我們業務中真正使用的樣子: 實現一個抽象類“AbstractPromiseExecution”
typedef void(^ExecutionSuccess)(id result);
typedef void(^ExecutionFailed)(NSError *error);
@interface AbstractPromiseExecution()
@property (nonatomic, strong) ExecutionSuccess success;
@property (nonatomic, strong) ExecutionFailed failed;
@end
@implementation AbstractPromiseExecution
- (WWPromise *(^)(id))promise{
return ^(id bizDatas){
return WWPromise.promise(^(ResolveFunc resolve, RejectFunc reject){
//設定success
self.success = ^(id result) {
resolve(result);
};
//設定failed
self.failed = ^(NSError *error) {
reject(error);
};
[self startExecute:bizDatas];
});
};
}
- (void)startExecute:(id) bizDatas{
//override by subclass
}
複製程式碼
從上面的抽象類很明顯的看出來,- promise方法會返回一個Promise例項,將業務的success和failed回撥賦值,同時會執行模板方法**- (void)startExecute:(id) bizDatas將需要傳遞的業務資料bizDatas傳遞下去,因此業務類只要繼承AbstractPromiseExecution然後複寫- (void)startExecute:(id) bizDatas**方法,在其中新增自己的業務邏輯,成功後呼叫 success 或者 failed,事例程式碼如下:
//子類 1
@interface FetchUserGroupInfoExecution : AbstractPromiseExecution
@end
@implementation FetchUserGroupInfoExecution
- (void)startExecute:(id) bizDatas{
//做非同步請求
[UserService requestUserGroup:^(id result){
strongSelf.success(result);
} error:^(NSError *errorInfo){
strongSelf.failed(errorInfo);
}];
}
@end
//子類 2
@interface CheckGroupAuthrizationExecution : AbstractPromiseExecution
@end
@implementation CheckGroupAuthrizationExecution
- (void)startExecute:(id) bizDatas{
//做非同步請求
[UserGroupService checkGroupAuthrization:^(id result){
strongSelf.success(result);
} error:^(NSError *errorInfo){
strongSelf.failed(errorInfo);
}];
}
@end
複製程式碼
這兩個子類是先獲取使用者的組資訊,然後在獲取這個組的許可權資訊,在業務使用時候如下:
FetchUserGroupInfoExecution *fetchExecution = [FetchUserGroupInfoExecution new];
CheckGroupAuthrizationExecution *checkExecution = [CheckGroupAuthrizationExecution new];
fetchExecution.promise(@{@"userId":@123})
.then(^(id res){
//Do business
return checkExecution.promise(res);
},^(NSError *error){
//Do error handling
}
).then(^(id res){
//Do business
return [WWPromise empty];
},^(NSError *error){
//Do error handling
}
);
複製程式碼
通過上面這樣的簡單封裝,以後的業務擴充套件也是非常的容易,編寫也很簡單,子類只要關心傳進來的資料做與自己相關的業務處理即可。