另一种解决方案是使用一些第三方库中提供的Promise 。我是RXPromise的作者,它实现了Promises/A+ 规范。
但是至少还有两个其他的 Objective-C 实现。
Promise 表示异步方法或操作的最终结果:
-(Promise*) doSomethingAsync;
Promise 完全替代了完成处理程序。此外,由于其明确的规范和底层设计,它具有一些非常有用的特性,使其特别容易处理相当复杂的异步问题。
您首先需要做的是将带有完成处理程序的异步方法包装到返回 Promise 的异步方法中:(有意地,您的方法在更方便的完成处理程序中返回最终结果和潜在错误)
例如:
- (RXPromise*) downloadAppInfo {
RXPromise* promise = [RXPromise new];
[self downloadAppInfoWithCompletion:^(id result, NSError *error) {
if (error) {
[promise rejectWithReason:error];
}
else {
[promise fulfillWithValue:result];
}
}];
return promise;
}
在这里,原来的异步方法变成了 Promise 的“解析器”。通过指定任务的最终结果或失败的原因,可以实现(成功)或拒绝(失败)承诺。然后,promise 将保存异步操作或方法的最终结果。
请注意,包装器是一个异步方法,它立即返回处于“待处理”状态的承诺。
最后,您通过使用方法或属性“注册”成功和失败处理程序来获得最终结果。then
周围的几个 Promise 库确实略有不同,但基本上它可能如下所示:
`promise.then( <success-handler>, <error-handler> )`
Promise/A+ 规范有一个简约的 API。以上内容基本上是实现 Promise/A+ 规范的全部需要 - 在许多简单的用例中通常就足够了。
然而,有时你需要更多——例如 OPs 问题,它需要在一组异步方法上“等待”,然后在所有方法都完成后做一些事情。
幸运的是,Promise 是一个理想的基本构建块,可以很容易地构建更复杂的辅助方法。
许多 Promise 库都提供了实用方法。因此,例如一个方法all
(或类似方法),它是一个返回 Promise 并将一组 Promise 作为输入的异步方法。当所有操作完成或失败时,返回的 Promise 将被解决。它可能如下所示:
首先构造一个promise数组,同时并行启动所有异步任务:
NSArray* tasks = @[
[self downloadAppInfo],
[self getAvailableHosts],
[self getAvailableServices],
[self getAvailableActions],
];
注意:在这里,任务已经在运行(并且可能完成)!
现在,使用一个完全符合上述内容的辅助方法:
RXPromise* finalPromise = [RXPromise all:tasks];
获得最终结果:
finalPromise.then(^id( results){
[self doSomethingWithAppInfo:results[0]
availableHosts:results[1]
availableServices:results[2]
availableActions:results[3]];
return nil;
}, ^id(NSError* error) {
NSLog(@"Error %@", error); // some async task failed - log the error
});
请注意,当返回的承诺将在方法中以某种方式解决时,将调用成功或失败all:
处理程序。
返回的承诺 ( finalPromise ) 将被解决,当
- 所有任务都成功成功,或者当
- 一项任务失败
对于案例 1),最终的 promise 将使用一个数组来解决,该数组包含每个相应异步任务的结果。
在情况 2) 中,最终的承诺将通过异步任务失败的错误来解决。
(注意:这里的几个可用库可能会有所不同)
RXPromise 库具有一些附加功能:
复杂的取消,它在承诺的非循环图中转发取消信号。
一种指定处理程序将在其中运行的调度队列的方法。队列可用于同步对共享资源的访问,例如,
self.usersPromise = [self fetchUsers];
self.usersPromise.thenOn(dispatch_get_main_queue(), ^id(id users) {
self.users = users;
[self.tableView reloadData];
}, nil);
与其他方法相比,该dispatch_group
解决方案存在阻塞线程的问题。这不是很“异步”。如果不是不可能的话,实现取消也是相当复杂的。
NSOperation
解决方案似乎喜忧参半。NSOperations
仅当您已经拥有并且没有在定义依赖项时需要考虑的完成处理程序时,它才可能是优雅的 - 否则,它会变得混乱和复杂。
到目前为止没有提到的另一个解决方案是Reactive Cocoa。恕我直言,这是一个很棒的库,可以让您解决几乎任何复杂性的异步问题。但是,它的学习曲线相当陡峭,并且可能会向您的应用程序添加大量代码。我猜,你遇到的 90% 的异步问题都可以通过可取消的 Promise 来解决。如果您有更复杂的问题,请查看 RAC。