Home What is DispatchWorkItem and how to use it
Post
Cancel

What is DispatchWorkItem and how to use it

DispatchWorkItem

Creating a DispatchWorkItem

You can use DispatchWorkItem to create a work to be performed on a dispatch queue.

1
2
3
4
5
let workItem = DispatchWorkItem {
  print("I'm doing some work")
}
DispatchQueue.main.asyncAfter(deadline: .now() + 4,
                              execute: workItem)

Canceling a DispatchWorkItem

To cancel a DispatchWorkItem you can call cancel on the dispatch work item object.

1
2
3
4
5
6
let workItem = DispatchWorkItem {
  print("I'm doing some work")
}
DispatchQueue.main.asyncAfter(deadline: .now() + 4,
                              execute: workItem)
workItem.cancel()

Keep in mind that if the DispatchWorkItem already started (in progress) then cancel has no affect.

Notify

To get notified when the DispatchWorkItem complete, you can use notify.

1
2
3
4
5
6
7
8
9
10
let workItem = DispatchWorkItem {
  print("I'm doing some work")
}
       
workItem.notify(queue: DispatchQueue.main) {
  print("I'm done")
}
       
DispatchQueue.main.asyncAfter(deadline: .now() + 4,
                              execute: workItem)

Perform

perform will start the execution of the work item block on the current thread synchronously.

1
2
3
4
5
6
7
8
9
10
11
let workItem = DispatchWorkItem {
  print("Let's do some work")
  sleep(5)
  print("I'm doing some work")
}
 
DispatchQueue.global().async {
  print("Before perform")
  workItem.perform()
  print("After perform")
}

This code will print in order:

1
2
3
4
5
Before perform
Let's do some work
// ... it will sleep for 5 seconds
I'm doing some work
After perform

Wait

Wait causes the caller to wait synchronously until the dispatch work item finishes executing.

1
2
3
4
5
6
7
8
9
10
11
12
let workItem = DispatchWorkItem {
  print("Let's do some work")
}
 
DispatchQueue.global().asyncAfter(deadline: .now() + 4,
                                  execute: workItem)
 
DispatchQueue.global().async {
  print("Before wait")
  workItem.wait()
  print("After wait")
}

This will print:

1
2
3
Before wait
Let's do some work
After wait

Quality of service

You give the DispatchWorkItem qos to tell the system how much important is your work, so, the higher is your qos the more the system will try to prioritizes the work.

We have 4 main types of quality of services:

  • User interactive: The quality-of-service class for user-interactive tasks, such as a work that the user is expecting to see the result directly.
  • User initiated: The quality-of-service class for tasks that is important but not as important as user interactive, maybe a user clicked on a button and he is waiting to see results.
  • Utility: The quality-of-service class for tasks that the user does not track actively, but he knows about them.
  • Background: The quality-of-service class for the tasks that the user is not aware of.

Another two:

  • unspecified: The absence of a quality-of-service class.
  • Default: The default quality-of-service class (between Utility and User initiated).

If you run the code below you will see that the system try to run all the Work item B before the A.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
let queue = DispatchQueue(label: "com.app.queue", attributes: .concurrent)
       
for _ in 0...100 {
  let workItemA = DispatchWorkItem(qos: .background) {
    print("Let's do some work A")
    sleep(5)
  }
           
  let workItemB = DispatchWorkItem(qos: .userInteractive) {
    print("Let's do some work B")
    sleep(5)
  }
           
  queue.async(execute: workItemA)
  queue.async(execute: workItemB)
}

So the code above will print something like:

1
2
3
4
5
6
7
8
9
10
Let's do some work B
Let's do some work B
Let's do some work B
Let's do some work B
Let's do some work B
......
...
Let's do some work A
Let's do some work A
Let's do some work A

but if you change the two work to have the same qos it will print something like this:

1
2
3
4
5
6
7
8
Let's do some work A
Let's do some work B
Let's do some work A
Let's do some work B
Let's do some work A
Let's do some work B
......
...

Barrier

One of the flags that you can pass to the work item is barrier, work item with barrier will act as a barrier block so no other work items will be executed while this barrier work item is being executed.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
let queue = DispatchQueue(label: "com.app.queue",
                          attributes: .concurrent)
let workItemA = DispatchWorkItem(flags: .barrier) {
  print("Let's do some work A")
  sleep(5)
  print("Done with the work A")
}
       
let workItemB = DispatchWorkItem {
  print("Let's do some work B")
  sleep(5)
}
       
let workItemC = DispatchWorkItem {
  print("Let's do some work C")
  sleep(5)
  print("Done with the work C")
}
       
queue.async(execute: workItemA)
queue.async(execute: workItemB)
queue.async(execute: workItemC)

The code above will print the following.

1
2
3
4
5
6
7
8
Let's do some work A
// Then it will not print anything for 5 seconds, then
Done with the work A
Let's do some work B
Let's do some work C
// then will not print anything for 5 seconds, then
Done with the work B
Done with the work C

As you can see from the code above the other two work item will be executed after the first work item done.

AssignCurrentContext

If you give the work item assignCurrentContext flag then this work item will inherit the current queue quality of service.

1
2
3
4
5
6
7
8
let queue = DispatchQueue(label: "com.app.queue",
                          qos: .background)
let workItem = DispatchWorkItem(flags: .assignCurrentContext) {
  print("Let's do some work")
  sleep(5)
  print("Done with the work")
}
queue.async(execute: workItem)

The above work item will be executed with background quality of service.

Detached

If you want your current work item to not inherent the queue properties like QOS, then you can pass the detached flag.

1
2
3
4
5
let queue = DispatchQueue(label: "com.app.queue", attributes: .concurrent)
let workItem = DispatchWorkItem(flags: .detached) {
  print("Let's do some work")
}
queue.async(execute: workItem)

-❤️~.

To read more about queues you can check the other articles published in this blog under concurrency category.

If you have any questions you can send me a message on Twitter or facebook. Also you can check my Github page or my Apps.

This post is licensed under CC BY 4.0 by the author.