Taskのsleep使いにくくない?

はじめに

completionHandlerで実装をしている処理をasync/awaitに直していく過程でローディングなどの表示確認のためにわざと遅延処理を入れて実行するケースが存在する。

async/awaitに直す前は以下のようにasyncAfterで定義するがasync/awaitに直した後Task.sleepでスリープさせたい場合引数の指定が難しい

//0.5秒間遅延させたい時
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5){...}
try await Task.sleep(nanoseconds: 500000000)

Extensionを作ろう

SwiftにはExtensionがあるのでなければ作れば良い、と言うわけで作った。
Appleが用意して欲しいところだけどなんですが、なぜnanosecondsの指定するメソッドしか用意されていないのか悩ましい

extension Task where Success == Never, Failure == Never {
    static func sleep(seconds duration: TimeInterval) async throws {
        let delay = UInt64(duration * 1000 * 1000 * 1000)
        try await Task.sleep(nanoseconds: delay)
    }
    
    static func sleep(millisecond duration: TimeInterval) async throws {
        let delay = UInt64(duration * 1000 * 1000)
        try await Task.sleep(nanoseconds: delay)
    }
    
    static func sleep(microseconds duration: TimeInterval) async throws {
        let delay = UInt64(duration * 1000)
        try await Task.sleep(nanoseconds: delay)
    }
}

正しく動くかの確認

Appleが用意しているnanosecondsの指定 + Extensionで追加したメソッド + asyncAfterで0.5秒遅延させてちゃんと同じくらいの秒数になっているかの検証を行った。

Task.detached {
    var start = Date()
    try await Task.sleep(nanoseconds: 500000000)
    print("nanoseconds:\(Date().timeIntervalSince(start))")
    start = Date()
    
    try await Task.sleep(microseconds: 500000)
    print("microseconds:\(Date().timeIntervalSince(start))")
    start = Date()
    
    try await Task.sleep(millisecond: 500)
    print("millisecond:\(Date().timeIntervalSince(start))")
    start = Date()
    
    try await Task.sleep(seconds: 0.5)
    print("seconds:\(Date().timeIntervalSince(start))")
    
    start = Date()
}

var start2 = Date()
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
    print("asyncAfter:\(Date().timeIntervalSince(start2))")
}

ログ出力

どの値も大体同じになっているのでExtensionの実装としては正しくできてそう🎉

asyncAfter:0.5502820014953613
nanoseconds:0.5682079792022705
microseconds:0.5497919321060181
millisecond:0.5499730110168457
seconds:0.549793004989624

tips

もともと全てのメソッドに1を指定して正しく動くか確認をしていたんですが、printするまでに時間がかかってしまうようで正しい桁で表示されませんでした、0.5秒で指定をした時0.05秒くらいずれていたのでprint表示を行うのにかかるのが0.05秒くらいかかるみたい。
ただnanosecondsに1を指定したりすると0.01とかになるのでsleepさせる時間によってprintにかかる時間も変わっていくようです🤔

参考文献

Delaying an asynchronous Swift Task