Delay and chain Swift operations with DispatchQueue
In this post, we’ll look at how DispatchQueue
can be used to delay and chain operations. We’ll also extend it with convenient functions that simplify these tasks.
Delaying operations
We can easily delaying operations with DispatchQueue
by using the asyncAfter
function:
let delay = DispatchTimeInterval.milliseconds(50)
DispatchQueue.main.asyncAfter(deadline: .now() + delay) {
print("I was delayed 50 milliseconds")
}
However, I think the deadline
is not that convenient, since you have to add .now()
to it. It’s probably correct from how a queue works, but it’s not that nice to use.
We can create an extension that makes delaying operations easier:
public extension DispatchQueue {
func asyncAfter(
_ interval: DispatchTimeInterval,
execute: @escaping () -> Void) {
asyncAfter(
deadline: .now() + interval,
execute: execute)
}
}
We can now delay operations like this:
let queue = DispatchQueue.main
queue.asyncAfter(.seconds(1)) {
print("I was delayed 1 second")
}
I think this is much cleaner, although I’d prefer the function to be called performAfter(...)
. I however chose this name to harmonize with the existing APIs.
Chaining operations
Chaining operations with DispatchQueue
is easy as well, using async
to perform another operation on the same or another queue:
let queue = DispatchQueue.main
queue.async {
{ print("Hello") }
queue.async { print(", world!") }
}
If an async operation returns a value, it can be passed to the chained operation like this:
let queue = DispatchQueue.main
queue.async {
let result = { return "Hello" }
queue.async { print(result + ", world!") }
}
This works well, but like asyncAfter
, I think this is lacking in usability as well.
Let’s create an extension that makes chaining operations easier:
public extension DispatchQueue {
func async<T>(
execute: @escaping () -> T,
then completion: @escaping (T) -> Void,
on completionQueue: DispatchQueue = .main) {
async {
let result = execute()
completionQueue.async {
completion(result)
}
}
}
}
We can now chain operations like this:
let queue = DispatchQueue.main
queue.async(
execute: { return "Hello"},
then: { print($0 + ", world!") },
on: .main
)
If you’re happy with using the default .main
completion queue, this can also be expressed as:
let queue = DispatchQueue.main
queue.async(execute: { return "Hello"}) {
print($0 + ", world!")
}
As with the delay operations, I’d have preferred to use perform
instead of async
and will perhaps over time make the functions more compact, but for now I’ve chosen to conform to the existing apis.
Source Code
I have added these extensions to my SwiftUIKit library. You can find the source code here. Feel free to try it out and let me know what you think!