Implementing tasks that can be canceled in Bolts Framework (BFTask)

  1. As you might know, BFTask is an implementation of the Future and Promises construct:
    "a future is a read-only placeholder view of a variable, while a promise is a writable, single assignment container which sets the value of the future".
    Basically, a BFTask is a Future: it is a read-only placeholder view of a variable.
    A BFTaskCompletionSource is a promise: it is a writable, single assignment container which sets the value of the future. (or an error - or cancels the task)
    The BFTask public interface remains read-only , hence it does not allow you to cancel it directly.
  2. This has the same answer as the previous question: BFTask is read-only and represents a read-only value. Exposing cancellation tokens would allow you to manipulate the task, which is contradictory to its nature.
  3. Let's take a look at it: https://github.com/BoltsFramework/Bolts-iOS/blob/master/Bolts/Common/BFCancellationToken.m The BFCancellationToken token just stores a state, which the BFTask can check. Your async task code can basically regularly check cancellationRequested is set to true, which allows you to manually cancel your task.

    Note: The Bolts Framework iOS docs say: "A task is kind of like a JavaScript Promise" which can be confusing, because it really is a Future. I think it was just named wrong in its Javascript origins.

There is a fairly useful implementation of cancellation tokens in Bolts, but for some reason it isn't documented at all outside of the header files. The key is the usage of the BFCancellationTokenSource. You need to keep a reference to the BFCancellationTokenSource in order to issue and cancel a BFCancellationToken.

In my example I have a particular function called cancellableFunction() that issues a bunch of tasks in succession. If the function is called again before the last call has completed, I want uncompleted tasks of the previous call to be cancelled.

The key here is to pass the token into each continueWith function call. If at any time the token is cancelled via the tokenSource, unreached successBlocks won't be executed. You can also check the status of cancellation via task.cancelled in each BFContinuationBlock (obviously will be false in success blocks).

Here is an example:

class ViewController: UIViewController {

   ...

   // instance reference to tokenSource so that it can be cancelled by any function in the ViewController
   var tokenSource: BFCancellationTokenSource?

   ...

   func cancellableFunction() -> BFTask {

      // First cancel the previous token
      tokenSource?.cancel()
      // Replace the previous TokenSource with a new one
      tokenSource = BFCancellationTokenSource()
      // Issue new Token from the new TokenSource
      let token = tokenSource!.token

      return functionThatReturnsBFTask().continueWithSuccessBlock({ (task:BFTask) -> AnyObject? in

         ...

         return nil
      }, cancellationToken: token).continueWithExecutor(BFExecutor.mainThreadExecutor(), successBlock: { (task:BFTask) -> AnyObject? in

         ...

         return nil
      }, cancellationToken: token).continueWithBlock({ (task:BFTask) -> AnyObject? in

         // Here you can perform an actions you want to take on cancellation
         if task.cancelled {

         }

         ...

         return nil
      }, cancellationToken: token)
   }

   ...

}

Use [self.bfTaskCancelationToken cancel]; code to cancel series BFTask

Register BFCancellationTokenSource

self.bfTaskCancelationToken = [BFCancellationTokenSource cancellationTokenSource];
[self.bfTaskCancelationToken.token registerCancellationObserverWithBlock:^{
    NSLog(@"task hasbeen Cancelled.....");
    //Do stuff on cancelation task

} ];

Implement Series BFTask

Note: there is cancellationToken:self.bfTaskCancelationToken.token code after [task continueWithBlock:^id(BFTask *task)

  [[[self showAlertProgressHud] continueWithBlock:^id(BFTask *taskLog) {


            BFTask *task = [BFTask taskWithResult:nil];
            for (int i=0; i<self.arrAssetPhotos.count; i++) {

                AIAssetPhoto *assetPhoto = self.arrAssetPhotos[i];
                task = [task continueWithBlock:^id(BFTask *task) {
                    // Return a task that will be marked as completed.
                    return [self processOnAssetPhoto:assetPhoto index:i completion:NULL];
                } cancellationToken:self.bfTaskCancelationToken.token];

            }

            return task;


        }] continueWithBlock:^id(BFTask *task) {

            // all asset photos process are done.
            return nil;
        }];

How to cancel continuous BFtask?

  //just by calling one simple method 
  [self.bfTaskCancelationToken cancel];