DispatchWorkItem not terminating function when .cancel() is called
Asked Answered
T

2

5

I have a series of HTTP requests made sequentially using Alamofire in a list of functions called in a main function, runTask() that I want to have the ability to stop. So, I set up the runTask() function call in a DispatchWorkItem for each of the task I need to run and store the work item in an array like so:

taskWorkItems.append(DispatchWorkItem { [weak self] in
    concurrentQueue!.async {
        runTask(task: task)
    }
})

Then, I iterate of the array of work items and call the perform() function like so:

for workItem in taskWorkItems {
    workItem.perform()
}

Finally, I have a button in my app that I want to cancel the work items when tapped, and I have the following code to make that happen:

for workItem in taskWorkItems {
    concurrentQueue!.async {
        workItem.cancel()

        print(workItem.isCancelled)
    }
}

workItem.isCancelled prints to true; however, I have logs set up in the functions called by runTask() and I still see the functions executing even though workItem.cancel() was called and workItem.isCancelled prints true. What am I doing wrong and how can I stop the execution of my functions?

Tied answered 3/10, 2018 at 20:28 Comment(2)
The workItem's isCancelled property is pointing out if the task is currently cancelled. If you cancel a WorkItem the task is totally cancelled if the execution has not started. If it started and is cancelled, the isCancelled property will indicate true, so you may implement your cancellation logic. There is a previous answer on how to cancel requests on Alamofire https://mcmap.net/q/423877/-cancel-a-request-alamofire You should execute the request.cancel() function into your Work Item logic if necessary.Explanation
@Explanation This was exactly what I needed, thank you!Tied
T
9

TLDR: calling cancel will stop tasks from executing if they have yet to be run, but won't halt something that's already executing.

Since the apple docs on this are threadbare...

https://medium.com/@yostane/swift-sweet-bits-the-dispatch-framework-ios-10-e34451d59a86

A dispatch work item has a cancel flag. If it is cancelled before running, the dispatch queue won’t execute it and will skip it. If it is cancelled during its execution, the cancel property return True. In that case, we can abort the execution

//create the dispatch work item
var dwi2:DispatchWorkItem?
dwi2 = DispatchWorkItem {
    for i in 1...5 {
        print("\(dwi2?.isCancelled)")
        if (dwi2?.isCancelled)!{
            break
        }
        sleep(1)
        print("DispatchWorkItem 2: \(i)")
    }
}
//submit the work item to the default global queue
DispatchQueue.global().async(execute: dwi2!)

//cancelling the task after 3 seconds
DispatchQueue.global().async{
    sleep(3)
    dwi2?.cancel()
}
Tambratamburlaine answered 3/10, 2018 at 20:35 Comment(0)
F
0

I hope my solution can help you.

var workItem:DispatchWorkItem?
workItem = DispatchWorkItem{
    while (true)
    {
       //Do you code as the loop
       usleep(10_000)
       // This condition is use to check the trigger that your code is done
       // The Loop must be break before you cancel
       if yourFinishedFlagIsDone {
               break
       }
    }
}
// Submit your work items and restore the finish flag
yourFinishedFlagIsDone = false
DispatchQueue.global().async(execute: workItem!)


// Add these lines of code to the event that you want to cancel the Dispatch Item
// Of Course, you need to enable your finish flag
yourFinishedFlagIsDone = true
DispatchQueue.global().async {
     workItem?.cancel()
}

Actually this solution is just a workaround method. One more, you have to remember that you have to put the DispatchWorkItem Initialization at the beginning of the event that you want to run. It is totally terminated when you call workItem?.cancel()

Faraday answered 13/2, 2023 at 9:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.