I realized that there is a difference between using DispatchQueue.main.asyncAfter(deadline: .now())
and perform(_:with:afterDelay:0)
when main queue is "busy".
Note that perform(_:with:afterDelay:)
is called from main queue in my situation.
Seem like DispatchQueue.main.asyncAfter(deadline: .now())
performs the task immediately at the next run loop without caring about main queue but perform(_:with:afterDelay:)
with 0 delay will wait and perform the task only when main queue is "free" (maybe it won't be called at the next run loop).
According to Apple document for perform(_:with:afterDelay:)
Specifying a delay of 0 does not necessarily cause the selector to be performed immediately. The selector is still queued on the thread’s run loop and performed as soon as possible.
I'm not sure that I understand them right so could anyone help me to explain exactly what is the difference under the hood between them? What does performed as soon as possible mean?
I found a same question here but seem like it's not what I want.
I created this standalone test to explore this topic.
class ViewController: UIViewController {
@objc func test1(_ info: Any) {
guard let str = info as? String else { return }
sleep(1)
print(str)
if str != "selector 3" {
self.perform(#selector(test1), with: "selector 3", afterDelay: 0)
}
DispatchQueue.main.asyncAfter(deadline: .now()) {
sleep(1)
print("dispatch 4 queued by \(str)")
}
}
@IBAction func test(_ sender: UIButton) {
print("begin test")
self.perform(#selector(test1), with: "selector 1", afterDelay: 0)
DispatchQueue.main.asyncAfter(deadline: .now()) {
DispatchQueue.main.asyncAfter(deadline: .now()) {
sleep(1)
print("dispatch 3")
}
sleep(1)
print("dispatch 1")
}
self.perform(#selector(test1), with: "selector 2", afterDelay: 0)
DispatchQueue.main.asyncAfter(deadline: .now()) {
sleep(1)
print("dispatch 2")
}
print("end test")
}
}
Resulting output:
begin test
end test
dispatch 1
dispatch 2
selector 1
selector 2
dispatch 3
dispatch 4 queued by selector 1
dispatch 4 queued by selector 2
selector 3
selector 3
dispatch 4 queued by selector 3
dispatch 4 queued by selector 3
Things to observe:
begin test
and end test
print before any other output showing that both perform(_:with:afterDelay:)
and DispatchQueue.main.asyncAfter
are queued and run later.DispatchQueue
s run before the perform
s even though they are queued in a different order.dispatch 3
doesn't jump ahead of selector 1
and selector 2
even though it is queued before dispatch 1
prints.Conclusions:
Dispatch.main.asyncAfter
and perform(_:with:afterDelay:)
queue their selector/closure to be performed later. Since you are running perform(_:with:afterDelay:)
on the main thread, it uses the main queue for its scheduling.Dispatch.main.asyncAfter(0)
calls are queued before perform(_:with:afterDelay:0)
calls when queued in the same run loop. Note: if any delay at all is added to the Dispatch.main.asyncAfter
, it will then be queued after the perform(_:with:afterDelay:)
. Try using .now() + .milliseconds(1)
, for example.