[swift-evolution] [Idea][Swift 3] Change 'DispatchQueue.after' to take a relative time + clock (with default). Maybe replace clocks with an enum?

Karl razielim at gmail.com
Wed Jul 13 11:18:32 CDT 2016


#1 - 

Currently, DispatchQueue.after() takes an absolute time as the “after” parameter. This makes it hard to understand how to use it; you need to go digging through generated interfaces to find out what a ‘DispatchTime’ is and how you construct it (by getting the .now() and adding a DispatchTimeInterval using one of the easily-missable operator overloads, if you were wondering).

Here is what DispatchQueue.after looks like now:

public func after(when: DispatchTime, qos: DispatchQoS = .unspecified, flags: DispatchWorkItemFlags = [], execute work: @convention(block) () -> Void)

So to use it, you have to type:

DispatchQueue.main.after(when: DispatchTime.now() + .milliseconds(250))   { /* do stuff */ }

I don’t believe this is a great fit with the Swift API Guidelines. I believe the name “after” already implies that the time is relative, the argument label should be dropped, and that nuances about the clock (basically, whether or not it pauses during system sleep) are edge-cases which can be handled for the majority of users with a sensible default value (or better yet, modelling via an enum or boolean switch — see #2). Additionally, There are overloads with “wallTime” parameter labels which seem only to duplicate type information (the monotonic and walltime clocks are actually split at the type level) and could be more concise and readable. The principle is that, ultimately, you should just be able to write the above code like this:

DispatchQueue.main.after(.milliseconds(250)) { /* do stuff */ }

Or

DispatchQueue.main.at(DispatchTime.now() + .seconds(3)) { /* do stuff */ }

It’s not something you use all the time (like .sync/.async), but I find that whenever I do need it I’m frustrated by how needlessly complex it is to decipher and use. I would find these methods much more obvious, I could figure out how to use them much more quickly, and I think I’d remember how to use them more quickly.

—

#2 - 

Actually, while writing this down it’s becoming clearer and clearer that the idea to split DispatchTime (the monotonic clock) and DispatchWallTime (the wall clock) at the type level is probably a bad idea.

Novice users are not going to understand what’s going on here - I expect most of them to default to the more generic-sounding “DispatchTime” without any idea of the implications of this. Perhaps we should look at replacing these clock variations with a more descriptive enum or boolean, rather than a separate type. For example:

struct DispatchTime {                       // replaces DispatchTime and DispatchWallTime
    let rawValue : dispatch_time_t
    let clock : Clock

    enum Clock { case monotonicClock; case wallClock }
}

This would not obsolete the discussion at the start about “after”. The name “after” still implies that I want something done at some duration relative to an absolute point in time (usually now).

Thoughts? 
There have been some really in-depth naming discussions on here recently, so I’m interested to hear what you make of it.

Karl


More information about the swift-evolution mailing list