这是一个符合 Promises/A+ 提案的 ObjectiveC 实现。完整的规范可以在http://promisesaplus.com/找到。
实现遵循所有的 Promise/A+ 规范,除了不适用于 ObjectiveC 的那些(特别是第 2.3.3 节)。
当前的实现尚未实现循环承诺链检测,将来将会添加对此的支持。
添加了对 Swift 的支持,请检查 CKSwiftPromise 框架(或如果您使用 Cocoapods,则检查 CKSwiftPromise 规范)
git clone https://github.com/cristik/CKPromise.git
CKPromise.xcproject
添加到您的项目/工作空间CKPromise
或/和 CKSwiftPromise
目标承诺的主要优势之一是它们有助于避免回调地狱。例如,让我们考虑一个添加书籍的序列,但在添加之前需要与服务器验证书籍的细节,插入后需要检索全部详情。
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSDictionary *bookData = @{@"name": @"Harry Potter"};
void (^failureBlock)(NSError *error) = ^(NSError *error) {
if([error.domain isEqual:NSURLErrorDomain]) {
// inform the user that there was a server communication problem
} else {
// inform the user that the book could not be added (e.g. it was already added by someone else)
}
};
[manager POST:@"http://myserver.com/validator/book"
parameters:bookData
success:^(AFHTTPRequestOperation *operation, id responseObject) {
[manager POST:@"http://myserver.com/book"
parameters:bookData
success:^(AFHTTPRequestOperation *operation, id responseObject) {
[manager GET:[@"http://myserver.com/book/" stringByAppendingString:responseObject]
parameters:nil
success:^(AFHTTPRequestOperation *operation, id responseObject) {
// inform the user that the book was added, move to the appropriate screen
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
failureBlock(operation, error);
}];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
failureBlock(operation, error);
}];
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
failureBlock(operation, error);
}];
这会导致 3 个 HTTP 请求调用,3 个成功+失败处理器,以及大量的缩进代码。承诺通过允许构建链条来解决这个问题,链条中的每个处理器在之前的处理完成后才会被调用。
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
NSDictionary *bookData = @{@"name": @"Harry Potter"};
return [manager POST:@"http://myserver.com/validator/book" parameters:bookData].promise.done(^{
return [manager POST:@"http://myserver.com/book" parameters:bookData].promise;
}).success(^(NSString *bookId){
NSString *url = [@"http://myserver.com/book/" stringByAppendingString:bookId];
return [manager GET:url parameters:nil].promise;
}).success(^(NSDictionary *bookDetails){
// inform the user that the book was added, move to the appropriate screen
}).failure(^(NSError *error) {
if([error.domain isEqual:NSURLErrorDomain]) {
// inform the user that there was a server communication problem
} else {
// inform the user that the book could not be added (e.g. it was already added by someone else)
}
});
另外,只需要一个失败回调,因为如果链条中的任何承诺失败,则链条将自动跳转到失败处理器,而不会执行其他的。
使用承诺还有许多其他好处,您可以在我的关于承诺的文章系列中了解更多信息:http://blog.cristik.com/2015/03/promises-and-objectivec-no-more-callback-hell/。
您可以通过使用 [[CKPromise alloc] init]
或使用便利初始化器 [CKPromise promise]
来创建一个承诺。
一旦您拥有一个承诺,您可以通过调用相应的方法来解析或拒绝它: resolve:
或 reject:
。
如果希望添加回调来跟踪承诺解析,可以使用以下内容:
then:
方法,允许传递两个回调,一个用于成功,一个用于失败。示例[promise then: ^{
NSLog(@"Succeeded");
} :^(NSError* err){
NSLog(@"Failed: %@", err);
}];
then
属性,它返回一个接受两个参数的块,这些参数是成功/失败的回调函数 示例promise.then(^(NSNumber *count){
NSLog(@"Number of records: %@", count);
}, ^{
NSLog(@"Failed");
});
then::
和 then
的分发队列变体,允许指定回调函数应在哪个分发队列上执行。默认情况下,回调函数会安排在主线程上,如有需要请使用这些方法。then:
、success:
、success
、failure:
、failure
、always:
、always
回调参数非常灵活,允许回调函数返回 ID 或者返回空 (void),接收一个参数或没有任何参数 (取决于你对承诺的结果或失败原因感兴趣)。因此,以下所有回调都合法
void^(){}
void^(id value){}
void^(NSNumber *result){}
id^(){return @15;}
id^(NSError *err){NSLog(@"Failed with error: %@", error); return [CKPromise rejected:err];}
这允许非常定制的承诺链,例如
[backgroundManagedObjectContext insertNewObject:@"User"].success(CKPromise* ^(NSString *uuid){
return [mainObjectConext fetchObjectOfType:@"User" withId:uuid];
}).success(void ^(NSManagedObjectContext *user){
//send the user to UI
}).failure(void ^(NSError* err){
NSLog(@"Encountered an error: %@", err);
})