Concurrency Programming with Operation Queues - Part 2

In my previous article, Concurrency Programming with Operation Queues - Part 1, I have shown you how to create operations and execute them. It’s time to dive deep and learn more about Operation Queues.

Adding Dependency

We can make the execution of Operations dependent. E.g., Let’s say we want to filter the data after it is retrieved from the server. We can divide this functionality into two operations. Operation A will retrieve the data from the server, and then Operation B will filter the data. We cannot let Operation B execute before Operation A, as the operation depends on the completion of Operation A.

Let’s look at a simple example of Adding Dependency.

let operationA = BlockOperation {
    print("Operation A")
}

let operationB = BlockOperation {
    print("Operation B")
}

// Adding Dependency for Operation B
operationB.addDependency(operationA)

let queue = OperationQueue()

// Adding Operation B before Operation A in the Queue
queue.addOperation(operationB) // Operation B
queue.addOperation(operationA) // Operation A

/* Output:
 Operation A
 Operation B
 */

In the code mentioned above, Operation B is added in the Queue first, and then Operation A is added. But if you look at the Output, Operation A executed before Operation B, and the reason for that is operationB.addDependency(operationA) this line of code. This line makes Operation B wait until Operation A completes execution. You can add multiple dependencies. Just make sure you don’t do something like the code shown below.

operationB.addDependency(operationA)
operationA.addDependency(operationB)

😅 This will lock your operations, as both of them are waiting for each other to execute.

Cancelling Operations 🙅🏽

Yes, unlike DispatchQueue, you can cancel operations. If the execution of the operation is pending, and you call cancel() on it, the cancelled operation won't execute, and it will be marked completed. Note, the operation reaches the completion and if you have any dependency like shown in the previous code example. Operation B will start executing directly.

Let's use the previous example and modify it to cancel Operation A and see what happens.

operationB.addDependency(operationA)

let queue = OperationQueue()

// Cancelling Operation A
operationA.cancel()

// Adding Operation B before Operation A in the Queue
queue.addOperation(operationB) // Operation B
queue.addOperation(operationA) // Operation A

/* Output:
 Operation B
 */

We have added operationA.cancel() just before adding the operations in the queue.

The output we get is "Operation B." Operation A is not executed. But sometimes we don't want the dependent operation to perform in case there is a cancellation. In the current issue, if Operation A is cancelled, we don't want Operation B to execute.

How do we do that?

Well, Operation class comes with a boolean property called isCancelled, which becomes true when the operation is cancelled. Let's see how we can use it to stop Operation B from executing.

// Injecting operationA inside operationB
let operationB = BlockOperation { [operationA]
    
    // Return if operationA is cancelled
    if operationA.isCancelled {
        print("Stop Operation B")
        return
    }
    
    print("Operation B")
}

/* Output:
 Stop Operation B
 */

If you look at the code shown above, we have modified the operation by injecting operationA in it, and with it, we check if the operation is cancelled. Depending on it, we take action.

Pretty simple, right?

In my next article, we will learn how to subclass Operation and override its function to suit our needs 😉.

I hope you guys enjoyed reading my article. There’s a lot more to learn together, so subscribe to stay updated about my upcoming articles.

If you have any suggestions or questions, Feel free to connect me on Twitter or Reddit. 😉

Building Peek and Pop with UICollectionView ...and UITableView

Concurrency Programming with Operation Queues - Part 1