这是符合 Promises/A+ 规范的 Objective-C 实现。完整规范可以在 http://promisesaplus.com/ 找到。
这个实现遵循所有 Promise/A+ 规范,除了不适用于 ObjectiveC 的那些(特别是第 2.3.3 节)。
当前的实现还没有实现循环 promise 链检测,这个功能将在以后添加。
添加了对 Swift 的支持,请检查 CKSwiftPromise 框架(如果您使用 Cocoapods,则请检查 CKSwiftPromise spec)。
git clone https://github.com/cristik/CKPromise.git
CKPromise.xcproject
添加到您的项目/工作区CKPromise
或/和 CKSwiftPromise
目标promises 的主要优势之一是它们可以帮助避免回调地狱。例如,考虑一个添加书籍的序列,但在添加它之前,需要与服务器验证书籍详情,添加后需要获取全部细节。
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 个成功+失败处理程序,以及大量的缩进代码。Promises 通过允许创建链来解决这个问题,每个处理器都在前一个完成后被调用。
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)
}
});
此外,只有一个失败回调是必需的,因为如果链中的任何 promise 失败,则链将自动前进到失败处理器,而不会执行其他处理器。
使用 promises 还有其他许多好处,您可以在我的关于 promises 的文章系列中了解更多信息:http://blog.cristik.com/2015/03/promises-and-objectivec-no-more-callback-hell/。
您可以使用 [[CKPromise alloc] init]
或使用便利初始化器 [CKPromise promise]
来创建一个 promise。
拥有一旦 promise,您可以调用适当的方来解决或拒绝它: resolve:
,或 reject:
。
如果您想添加回调来跟踪 promise 的解决,可以使用以下
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),并接收一个参数或没有参数(取决于你是否有兴趣知道promise的结果或失败原因)。因此,所有以下回调都是合法的
void^(){}
void^(id value){}
void^(NSNumber *result){}
id^(){return @15;}
id^(NSError *err){NSLog(@"Failed with error: %@", error); return [CKPromise rejected:err];}
这允许非常定制的promise链,例如
[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);
})