Bolts/BFTask

Bolts

简介

自从Parse加盟Facebook后,他们发现很多细小的功能都分别在各自的sdk中实现了。于是他们决定开发一个更底层的库协调他们的sdk之间的工作。现在已经将这个库开源,这就是Bolts。

组件

task

第一个组件就是task,这是为了解决async callbacks而产生的。BFTask的相关源码并不多,主要是以BFTask, BFTaskCompletionSource, BFExector 这三个为主。

BFTask的使用非常简单,如果开发过程中有一个可以异步执行的方法,那么就可以执行这个方法并且返回一个BFTask指针,然后在continuation block中处理这个异步方法执行后的操作。

  • 基本用法

    
    // Objective-C
    - (BFTask *) fetchAsync:(PFObject *)object {
     / *
      * 创建一个标示BFTask是否完成的类,BFTaskCompletionSource本身就含有一个BFTask.
      * 在下面的代码中object完成fetchInBackgroundWithBlock操作后,对taskSource中的task进行设置,标示这个task的完成情况,用于外部对这个task的后续处理。
      */
    BFTaskCompletionSource *taskSource = [BFTaskCompletionSource taskCompletionSource];
      
    [object fetchInBackgroundWithBlock:^(PFObject *object, NSError *error) {
        if (!error) {
          [taskSource setResult:object];
        } else {
          [taskSource setError:error];
        }
      }];
      
     return taskSource.task;
    }
    
    // Objective-C
    [[self fetchAsync:obj] continueWithBlock:^id(BFTask *task) {
        
        if (task.result) {
            // fetchAsync task 成功
        }
        else if (task.error) {
            // fetchAsync task 失败
        }     
      return nil;
    }];
    
    // Objective-C
    [[self fetchAsync:obj] continueWithSuccessBlock:^id(BFTask *task) {
      // 如果只需要关心成功情况可以使用continueWithSuccessBlock
      return nil;
    }];
    
    
  • 链式用法

    
    // Objective-C
    PFQuery *query = [PFQuery queryWithClassName:@"Student"];
    [query orderByDescending:@"gpa"];
    [[[[[self findAsync:query] continueWithSuccessBlock:^id(BFTask *task) {
      NSArray *students = task.result;
      PFObject *valedictorian = [students objectAtIndex:0];
      [valedictorian setObject:@YES forKey:@"valedictorian"];
      return [self saveAsync:valedictorian];
    }] continueWithSuccessBlock:^id(BFTask *task) {
      PFObject *valedictorian = task.result;
      return [self findAsync:query];
    }] continueWithSuccessBlock:^id(BFTask *task) {
      NSArray *students = task.result;
      PFObject *salutatorian = [students objectAtIndex:1];
      [salutatorian setObject:@YES forKey:@"salutatorian"];
      return [self saveAsync:salutatorian];
    }] continueWithSuccessBlock:^id(BFTask *task) {
      // Everything is done!
      return nil;
    }];
    
    
  • 串行任务

    
    / *
      * 通过PFQuery进行异步查询,然后将查询结果异步删除,并且删除操作是串行的.
     */
    
    // Objective-C
    PFQuery *query = [PFQuery queryWithClassName:@"Comments"];
    [query whereKey:@"post" equalTo:@123];
    
    [[[self findAsync:query] continueWithBlock:^id(BFTask *task) {
      NSArray *results = task.result;
    
      // 创建一个开始的任务,之后的每一个deleteAsync操作都会依次在这个任务之后顺序进行.
     BFTask *task = [BFTask taskWithResult:nil];
     for (PFObject *result in results) {
        // For each item, extend the task with a function to delete the item.
        task = [task continueWithBlock:^id(BFTask *task) {
          // Return a task that will be marked as completed when the delete is finished.
          return [self deleteAsync:result];
        }];
      }
      // 返回的是最后一个deleteAsync操作的task
      return task;
    }] continueWithBlock:^id(BFTask *task) {
      // Every comment was deleted.
      return nil;
    }];
    
    
  • 并行任务

    
    / *
      * 通过PFQuery进行异步查询,然后将查询结果异步删除,并且删除操作是并行的.
     */
     
    // Objective-C
    PFQuery *query = [PFQuery queryWithClassName:@"Comments"];
    [query whereKey:@"post" equalTo:@123];
    
    [[[self findAsync:query] continueWithBlock:^id(BFTask *results) {
      // Collect one task for each delete into an array.
      NSMutableArray *tasks = [NSMutableArray array];
      for (PFObject *result in results) {
        // Start this delete immediately and add its task to the list.
        [tasks addObject:[self deleteAsync:result]];
      }
      
      // 所有的删除任务合在一起本身也是一个任务,删除任务之前是并行的
      return [BFTask taskForCompletionOfAllTasks:tasks];
    }] continueWithBlock:^id(BFTask *task) {
      // Every comment was deleted.
      return nil;
    }]; 
        
    
  • 任务执行者

    BFExecutor 是 BFTask的执行者,默认的BFExecutor是立即在当前线程中执行的,但是如果call stack太深,会异步dispatch到global queue上去执行。
    如果不用默认的执行者,还可以指定执行queue,或者是NSOperationQueue,包括主线程。

    
    // 创建 BFExecutor
    BFExecutor *myExecutor = [BFExecutor executorWithBlock:^void(void(^block)()) {
      dispatch_async(dispatch_get_main_queue(), block);
    }];
    
    BFExecutor *myExecutor = [BFExecutor mainThreadExecutor];
    
    // And use the Main Thread Executor like this. The executor applies only to the new
    // continuation being passed into continueWithBlock.
    [[self fetchAsync:object] continueWithExecutor:myExecutor withBlock:^id(BFTask *task) {
        myTextView.text = [object objectForKey:@"name"];
    }];
    
    
  • 任务取消

    我们在直接使用GCD的时候很难把一个block任务取消。BFCancellationTokenSource就很方便的实现了任务取消功能。

      /*
      * 执行任务的时候传入BFCancellationTokenSource,在任务还没有执行的时候设置取消标示,
      * 该任务的block不会被执行
      */
      
     BFCancellationTokenSource *cts = [BFCancellationTokenSource cancellationTokenSource];
     
     /// 任务延迟执行100毫秒
     BFTask *task = [BFTask taskWithDelay:100];
        
     task = [task continueWithExecutor:[BFExecutor immediateExecutor]
                  block:^id(BFTask *t) {
                    NSLog(@"Continuation block should not be triggered");
    
                    return nil;
                }
                    cancellationToken:cts.token];
      
     /// 设置取消标示  
    [cts cancel];
    /// 等待任务完成 (这个代码一般不在主线程中使用)
    [task waitUntilFinished];
    
    
    
    /*
      * 也可以在continueWithBlock中自己实现对cancellationToken的判断,取消任务
      */
    - (void)doSomethingComplicatedAsync:(MYCancellationToken *)cancellationToken {
        [[self doSomethingAsync:cancellationToken] continueWithBlock:^{
            if (cancellationToken.isCancelled) {
                return [BFTask cancelledTask];
            }
            // Do something that takes a while.
            return result;
        }];
    }
    
    // Somewhere else.
    MYCancellationToken *cancellationToken = [[MYCancellationToken alloc] init];
    [obj doSomethingComplicatedAsync:cancellationToken];
    
    // When you get bored...
    [cancellationToken cancel];
    
    
  • 关键源码解析

    BFTask的关键代码就是下面这个函数。

        
    /*
     * 在指定的BFExecutor中执行block代码
     */
    - (BFTask *)continueWithExecutor:(BFExecutor *)executor
                           block:(BFContinuationBlock)block
               cancellationToken:(nullable BFCancellationToken *)cancellationToken {
               
    // 创建BFTaskCompletionSource,返回source的task。本次执行block的操作也是一个需要返回的任务。
    BFTaskCompletionSource *tcs = [BFTaskCompletionSource taskCompletionSource];
    
    // 定义执行的executionBlock
    dispatch_block_t executionBlock = ^{
          // 如果cancellationToken标示位取消,就直接把tcs中的task设置为取消,不会继续执行BFContinuationBlock,实现了对block的取消操作。
        if (cancellationToken.cancellationRequested) {
            [tcs cancel];
            return;
        }
    
        id result = nil;
    

pragma clang diagnostic push

pragma clang diagnostic ignored "-Wdeprecated-declarations"

    if (BFTaskCatchesExceptions()) {
        @try {
                // 执行BFContinuationBlock,如果block中抛出异常就把tcs中的task设置tcs中task为异常,不再继续执行
            result = block(self);
        } @catch (NSException *exception) {
            tcs.exception = exception;
            return;
        }
    } else {
        result = block(self);
    }

pragma clang diagnostic pop

     // 如果block也返回一个BFTask,如果BFTask已经结束了,执行setupWithTask(BFTask)
     // 以BFTask的结果作为tcs的结果。如果BFTask没有结束,就等BFTask结束后再
     // 执行setupWithTask block
    if ([result isKindOfClass:[BFTask class]]) {

        id (^setupWithTask) (BFTask *) = ^id(BFTask *task) {
            if (cancellationToken.cancellationRequested || task.cancelled) {
                [tcs cancel];

pragma clang diagnostic push

pragma clang diagnostic ignored "-Wdeprecated-declarations"

            } else if (task.exception) {
                tcs.exception = task.exception;

pragma clang diagnostic pop

            } else if (task.error) {
                tcs.error = task.error;
            } else {
                tcs.result = task.result;
            }
            return nil;
        };

        BFTask *resultTask = (BFTask *)result;

        if (resultTask.completed) {
            setupWithTask(resultTask);
        } else {
            [resultTask continueWithBlock:setupWithTask];
        }

    } else {
        tcs.result = result;
    }
};

BOOL completed;

// 如果self没有completed,就把执行executionBlock的操作放在callbacks里。
// task中对callbacks的访问需要加锁
@synchronized(self.lock) {
    completed = self.completed;
    if (!completed) {
        [self.callbacks addObject:[^{
            [executor execute:executionBlock];
        } copy]];
    }
}
if (completed) {
    [executor execute:executionBlock];
}

return tcs.task;

}

```

你可能感兴趣的:(Bolts/BFTask)