Makis
Makis

Reputation: 1244

GCD - asyncAfter: How to run it synchronously

I have just started reading swift and i'm currently confused of how to use threading correctly.

What i'm trying to achieve in the following block of code, is to execute the print statements inside the dispatchers, but i want to do it in order. The problem that i have, is that of course i want to do this in a background thread than the main since this is a long task, and at the same time to execute it in order while i'm giving delay in the execution. The current block executes each of the cases all together.

I have also took a look in Timer and Semaphores but without any results.

Any help or explanation of what i'm doing wrong or what should i approach will be appreciated.

let formattedSeries = ["a", "a", "b"]
let dispatchQueue = DispatchQueue(label: "taskQueue")
let a = 1000
let b = 5000
for (index, letter) in (formattedSeries.enumerated()){

    switch letter {
    case "a":
        dispatchQueue.asyncAfter(deadline: .now() + .milliseconds(a), execute: {
            print("a executed")           
        })      
        break
    case "b":
        dispatchQueue.asyncAfter(deadline: .now() + .milliseconds(b), execute: {
            print("b executed")
        })
        break
    default:
        print("default")
    }
}

Upvotes: 0

Views: 297

Answers (2)

matt
matt

Reputation: 535138

You can use a dispatch group to force the evaluation of the next letter to wait for evaluation of the previous letter:

let dispatchGroup = DispatchGroup()
let dispatchQueue = DispatchQueue(label: "taskQueue")
let a = 1000
let b = 5000
let formattedSeries = "abbaabba"
print("start", Date().timeIntervalSince1970)
for (index, letter) in (formattedSeries.enumerated()){
    dispatchGroup.enter()
    switch letter {
    case "a":
        dispatchQueue.asyncAfter(deadline: .now() + .milliseconds(a), execute: {
            print("a executed", Date().timeIntervalSince1970)
            dispatchGroup.leave()
        })
        break
    case "b":
        dispatchQueue.asyncAfter(deadline: .now() + .milliseconds(b), execute: {
            print("b executed", Date().timeIntervalSince1970)
            dispatchGroup.leave()
        })
        break
    default:
        print("default")
    }
    dispatchGroup.wait()
}

I've added some extra output to prove that the intervals are correct. The output is

start 1580060250.3307471
a executed 1580060251.389974
b executed 1580060256.889923
b executed 1580060262.2758632
a executed 1580060263.372933
a executed 1580060264.373787
b executed 1580060269.37443
b executed 1580060274.375314
a executed 1580060275.4726748

which proves that we evaluated the letters in order, and that the async_after intervals elapse between the prints.

Upvotes: 1

vadian
vadian

Reputation: 285079

To execute the tasks in order you need an asynchronous operation.

  1. Use the class AsynchronousOperation provided in this answer
  2. Create a serial OperationQueue and set maxConcurrentOperationCount to 1
  3. Subclass AsynchronousOperation and put the dispatchQueue.asyncAfter tasks into the main() method of the subclass. Call finish() in the closure (before or after print("a executed"))
  4. Add the operations to the serial operation queue

Upvotes: 1

Related Questions