OperationQueues in Swift - More control to your async Operations with asynchronous dependency graph

Subscribe to my newsletter and never miss my upcoming articles

Hello ladies and gentlemen, Leo here.

Today we'll explore the OperationQueue class and how it can help you improve your async operations. The scope is introductory therefore we won't going to explore every possibility with OperationQueues, the focus is the basics of the API.

Let's go!

OperationQueue

What is an OperationQueue? As you can see here in the OperationQueue class docs, an OperationQueue is :

A queue that regulates the execution of operations.

But wait a minute, what is an Operation?

Again, in the Operation class docs:

An abstract class that represents the code and data associated with a single task.

So you have a queue and the operations that you will add to it. You can do synchronous and asynchronous tasks on the queue, today we'll just look to the async ones.

The problem

Imagine that you have some async processing that when it finishes trigger other async processes and so on.

Before we attack the problem let's check the code below:

let operation1 = BlockOperation { //1
    for x in 0...5 {
        print(x)
    }
}

let operation2 = BlockOperation { //1
    for x in 11...15 {
        print(x)
    }
}

let operation3 = BlockOperation { //1
    for x in 21...25 {
        print(x)
    }
}

let operationQueue = OperationQueue() //2

operationQueue.addOperations([operation1,operation2,operation3], waitUntilFinished: false) //3

The (1) mark is one way to create the Operation class we discuss above, those are only computing number but you can insert and even create your own Custom Operation classes (it's not the scope of this post, maybe in the future). By default the OperationBlock isn't asynchronous for it's own so we'll have to put it in OperationQueue to process asynchronously like we are doing in the (3) mark. The (2) only create an OperationQueue.

One more thing: did you notice that we have a waitUntilFinished equals false when adding Operations? This will not block our thread making the program continues while the operations are running. You can set to true if you want a blocking operation.

Let's run and see the result:

Screen Shot 2020-10-29 at 08.12.12.png

And every time you run this, will be a different result. But now it's when the magic happens, what if I wanted to have a little more control over this execution? Now it's when the Operation and OperationQueue rise and shine, the API provide us the right tools to achieve that.

Asynchronous Dependency Graph

On the same example you can add some Operation dependencies, this way, building a asynchronous dependency graph. Check the code below:

let operation1 = BlockOperation {
    for x in 0...5 {
        print(x)
    }
}

let operation2 = BlockOperation {
    for x in 11...15 {
        print(x)
    }
}
operation2.addDependency(operation1) // 1


let operation3 = BlockOperation {
    for x in 21...25 {
        print(x)
    }
}
operation3.addDependency(operation1) // 1 


let operationQueue = OperationQueue()

operationQueue.addOperations([operation1,operation2,operation3], waitUntilFinished: false)

Check the (1) marks in the code. We are creating a dependency here saying to the queue: Hey Queue, you will only run operation 2 and 3 WHEN the operation 1 is finished, because they are dependent of the first one.

And the result is:

Screen Shot 2020-10-29 at 08.22.42.png

You can see that first the queue completes all the operation1 processing to after that asynchronous run the other 2 operations in it.

Did you notice that you can even implement your own DispatchGroup with OperationQueue? Simply put one Operation that depends on every other Operation in the queue and it's done!

Operation properties

There are two interesting things to notice in Operation class, the qualityOfService and queuePriority properties.

The qualityOfService properties affect the priority with which an operation object is given access to system resources such as CPU time, network resources, disk resources, and so on.

And the queuePriority contains the relative priority of the operation. This value is used to influence the order in which operations are dequeued and executed. Priority values should not be used to implement dependency management among different operation objects. You can use addDependecy() method to do that.

Tweaking OperationQueues

The final thoughts about this introduction to OperationQueues are some properties you can use to control the async execution of your code.

The maxConcurrentOperationCount property of the queue can give you control on guess what? How many concurrent operations you can have each time. So imagine that you have 3 items in the queue like the example above, if you set this property to 2, they will run two random operations at a time and only when one of the finishes it works it will initiate the third one.

let operation1 = BlockOperation {
    for x in 0...6 {
        print(x)
    }
}


let operation2 = BlockOperation {
    for x in 11...15 {
        print(x)
    }
}

let operation3 = BlockOperation {
    for x in 21...25 {
        print(x)
    }
}

let operationQueue = OperationQueue()

operationQueue.maxConcurrentOperationCount = 2 // this property 

operationQueue.addOperations([operation1,operation2,operation3], waitUntilFinished: false)

Resulting in:

Screen Shot 2020-10-29 at 08.42.48.png

As you can see, the operation3 only started after the completion of the operation2.

Conclusion

This brief introductory article shows that operation queue gives you the power to easy create asynchronous dependency graph operations and it's a very versatile tool to have in your Swift toolbox.

Of course you can achieve the same results using pure GCD functions because OperationQueue is only a wrapper of it with some KVO and KVC magic in it. You can even get the dispatchQueue from the OperationQueue using underlyingQueue property if you want to.

Hope you enjoy reading as I enjoy writing, if you have any feedbacks give me the opportunity to know!

Thanks for the reading and... that's all folks!

Credits: image

Comments (1)

Liana's photo

Lately, it's hard enough for me to do something, I have constant apathy, I can't work at a normal job because it seems to me that it will not bring me any benefit in my life and I will have to sit in the office all my life and earn a penny, and I live like real money online casino that completely I don’t want so I found a new way of earning for myself that helps me and some of my friends to make money, we can assume that this way is absolutely no problem this casino in the world of casinos there are many different games and many different ways of winning that really work and are really very good