Bolts framework iOS 筆記
http://humanhighway.logdown.com/posts/179270-study-on-the-bolts
Bolts 是由 Facebook and Parse open source 的 Framework. 主要实现是为了解决 async callbacks 问题的 Promise Pattern. Promise / Future 在很多语言或 library 都有实现。 ( JQuery, AngularJS, Java, Dart ...etc )
虽说是 Framework 但用法并不难而且类数也不多。主要是以 BFTask, BFTaskCompletionSource, BFExector 这三个为主要。
假设我们要建立一个 testAsync function, 必须要在最后回传 BFTask , 建立 task 的方式则使用 BFTaskCompletionSource 的静态方法来建立 task 并设定相关参数。
(BFTask *)testAsync
NSLog(@"testAsync");
BFTaskCompletionSource *source = [BFTaskCompletionSource taskCompletionSource];
source.result = @"test async complete";
return source.task;
// or you can use taskWithResult
// return BFTask taskWithResult: @"test async complete"];
}
BFTaskCompletionSource 是 BFTask 的 proxy object(代理对象)。当 function 完成时设定 task.result, 执行后则视为 task complete. task.result 是 id 动态型别,可以将处理完后需要的结果设定到 task.result. function 执行时导致错误失败设定 task.error, 如有执出现例外设定 task.exception, 如要取消 task 呼叫 [task cancel].
(task complete、task.result、task.error、task.exception、task cancel)
无论是 error/exception/cancel 执行完后皆视为 task complete. 另外, task 提供了一些方法来判断目前状况 isCancelled / isCompleted / error / exception. 也可以直接使用 [BFTask taskWithResult] / [BFTask taskWithError] / [BFTask taskWithException] / [BFTask cancelledTask] 静态方法直接建立 task 来做相关处理。
实现 Async function 之后, 可以一个接着一个将要调用的函数串起来称之为 Chain. 在每个函数回调的 task 执行 contineWithBlock / continueWithSuccessBlock
[[[[[self testAsync] continueWithBlock:^id(BFTask *task) {
return [self testAsync1];
}] continueWithBlock:^id(BFTask *task) {
return [self testAsync2];
}] continueWithBlock:^id(BFTask *task) {
return [self testAsync3];
}] continueWithBlock:^id(BFTask *task) {
NSLog(@"task in chain complete");
return nil;
}];
另外还能以 Series / Parallel 的方式来执行 Task.
Series: 以串行方式回调 task 执行 continueWithBlock
[[[self testAsync] continueWithBlock:^id(BFTask *task) {
BFTask *_task = [BFTask taskWithResult:nil];
for (int i =0; i < 3; i++) {
task = [task continueWithBlock:^id(BFTask *task) {
return [self testAsync];
}];
}
return _task;
}] continueWithBlock:^id(BFTask *task) {
NSLog(@"tasks execute in series complete");
return nil;
}];
Parallel : 将所有 task 塞进一个 array 后丢给 taskForCompletionOfAllTasks 作处理.
[[[self testAsync] continueWithBlock:^id(BFTask *task) {
NSMutableArray *tasks = [NSMutableArray new];
for (int i = 0 ; i < 3; i++) {
[tasks addObject:[self testAsync]];
}
[tasks addObject:[self testAsync]];
return [BFTask taskForCompletionOfAllTasks:tasks];
}] continueWithBlock:^id(BFTask *task) {
NSLog(@"tasks execute in parallel complete");
return nil;
}];
BFTask 主要是通过 BFExecutor 来执行, 默认是通过 [BFExecutor defaultExecutor] , 另外还有 immediateExecutor / mainThreadExecutor 可供使用,基于 GCD 实现。
immediateExecutor : 是直接以 GCD dispatch_once 执行。
mainThreadExecutor : 以 GCD dispatch_once 执行, 检查是否为 isMainThread, 如果不是则调用 dispatch_async(dispatch_get_main_queue(), block); 延后执行。
defaultExecutor : 以 GCD dispatch_once 执行, 会检查 current thread 的 threadDictionary objectForKey:kBFTaskDepthKey 索取出的 depth 是否超过 20 个, 如果超过, 则 dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), block); 延后执行。否则会在当前 Thread 将 depth + 1, try-catch 执行完毕再 -1。
[[self testAsync] continueWithExecutor:[BFExecutor mainThreadExecutor] withBlock:^id(BFTask *task) {
NSLog(@"task execute");
return nil;
}
简单好上手, 应该很容易整合到现有的项目里。
相关资料
Mine
测试准备
- (OSSTask *)testAsync
{
NSLog(@"testAsync");
OSSTaskCompletionSource *source = [OSSTaskCompletionSource taskCompletionSource];
// [source setResult:@"test async complete"];
source.result = @"test async complete";
return source.task;
// return [OSSTask taskWithResult:@"test async complete"];
}
- (OSSTask *)testAsync1
{
NSLog(@"testAsync1");
return [OSSTask taskWithResult:@"test async1 complete"];
}
- (OSSTask *)testAsync2
{
NSLog(@"testAsync2");
return [OSSTask taskWithResult:@"test async2 complete"];
}
- (OSSTask *)testAsync3
{
NSLog(@"testAsync3");
return [OSSTask taskWithResult:@"test async3 complete"];
}
[[[[[self testAsync] continueWithBlock:^id _Nullable(OSSTask * _Nonnull task) {
NSLog(@"%@", task.result);
return [self testAsync1];
}] continueWithBlock:^id _Nullable(OSSTask * _Nonnull task) {
NSLog(@"%@", task.result);
return [self testAsync2];
}] continueWithBlock:^id _Nullable(OSSTask * _Nonnull task) {
NSLog(@"%@", task.result);
return [self testAsync3];
}] continueWithBlock:^id _Nullable(OSSTask * _Nonnull task) {
NSLog(@"task in chain complete");
NSLog(@"%@", task.result);
return nil;
}];
2017-02-21 11:38:59.346161 Test-Project[847:195718] testAsync
2017-02-21 11:38:59.346771 Test-Project[847:195718] test async complete
2017-02-21 11:38:59.346845 Test-Project[847:195718] testAsync1
2017-02-21 11:38:59.347243 Test-Project[847:195718] test async1 complete
2017-02-21 11:38:59.347301 Test-Project[847:195718] testAsync2
2017-02-21 11:38:59.347696 Test-Project[847:195718] test async2 complete
2017-02-21 11:38:59.347862 Test-Project[847:195718] testAsync3
2017-02-21 11:38:59.348012 Test-Project[847:195718] task in chain complete
2017-02-21 11:38:59.348248 Test-Project[847:195718] test async3 complete
测试串行
/** Series: 以串行方式回调 task 执行 continueWithBlock */
[[[self testAsync] continueWithBlock:^id _Nullable(OSSTask * _Nonnull task) {
OSSTask *_task = [OSSTask taskWithResult:nil];
for (int i = 0; i < 3; i++) {
NSLog(@"%@", task.result);
task = [task continueWithBlock:^id _Nullable(OSSTask * _Nonnull task) {
return [self testAsync1];
}];
}
return _task;
}] continueWithBlock:^id _Nullable(OSSTask * _Nonnull task) {
NSLog(@"tasks excute in series complete");
NSLog(@"%@", task.result);
return nil;
}];
2017-02-21 11:42:48.077688 Test-Project[855:196733] testAsync
2017-02-21 11:42:48.078335 Test-Project[855:196733] test async complete
2017-02-21 11:42:48.078422 Test-Project[855:196733] testAsync1
2017-02-21 11:42:48.078843 Test-Project[855:196733] test async1 complete
2017-02-21 11:42:48.078931 Test-Project[855:196733] testAsync1
2017-02-21 11:42:48.079150 Test-Project[855:196733] test async1 complete
2017-02-21 11:42:48.079291 Test-Project[855:196733] testAsync1
2017-02-21 11:42:48.079626 Test-Project[855:196733] tasks excute in series complete
2017-02-21 11:42:48.079918 Test-Project[855:196733] (null)
测试并行
/** Parallel : 将所有 task 塞进一个 array 后丢给 taskForCompletionOfAllTasks 作处理. */
[[[self testAsync] continueWithBlock:^id _Nullable(OSSTask * _Nonnull task) {
NSMutableArray *tasks = [NSMutableArray new];
for (int i = 0; i < 3; i ++ ) {
[tasks addObject:[self testAsync1]];
}
[tasks addObject:[self testAsync2]];
return [OSSTask taskForCompletionOfAllTasks:tasks];
}] continueWithBlock:^id _Nullable(OSSTask * _Nonnull task) {
NSLog(@"tasks execute in parallel complete");
NSLog(@"%@", task.result);
return nil;
}];
2017-02-21 11:44:14.436728 Test-Project[858:197114] testAsync
2017-02-21 11:44:14.437287 Test-Project[858:197114] testAsync1
2017-02-21 11:44:14.437355 Test-Project[858:197114] testAsync1
2017-02-21 11:44:14.437401 Test-Project[858:197114] testAsync1
2017-02-21 11:44:14.437445 Test-Project[858:197114] testAsync2
2017-02-21 11:44:14.437735 Test-Project[858:197114] tasks execute in parallel complete
2017-02-21 11:44:14.437844 Test-Project[858:197114] (null)