【Swift Concurrency】async/awaitの使い方!非同期処理の実装

この記事からわかること

  • Swift Concurrencyとは?
  • async/await使い方
  • 非同期処理実装方法
  • completionHandler(コールバック関数)との違い

index

[open]

\ アプリをリリースしました /

みんなの誕生日

友達や家族の誕生日をメモ!通知も届く-みんなの誕生日-

posted withアプリーチ

環境

公式リファレンス:Concurrency
公式ドキュメント:Concurrency

Swift Concurrencyとは?

Swift Concurrency(同時実効性)とはiOS15(Swift 5.5)から導入された仕組みの1つで非同期プログラミングをより利用しやすくするための機能を提供しています。asyncawaitキーワードはSwift Concurrencyから提供されており、非同期処理を実装する際に利用されるcompletionHandler(コールバック関数)の弱みである可読性の低下を解消することができるようになりました。

async/awaitの使い方

最初にasync/awaitキーワードの使い方とcompletionHandler(コールバック関数)を使用した場合との違いを見てみます。例えとして「サーバーからデータを取得する処理:fetchData」をそれぞれで実装してみます。

completionHandler(コールバック関数)

func fetchData(completion: @escaping (Result<Data, Error>) -> Void) {
    sleep(2)
    let data = Data() // 本来ならサーバーからデータ取得(成功 or 失敗)
    completion(.success(data))
}

print("START")

fetchData { result in
    switch result {
    case .success(let data):
        print("Data received: \(data)")
    case .failure(let error):
        print("Error: \(error)")
    }
}

おすすめ記事:【Swift】Result型の使い方!非同期のエラー処理

async/await

func fetchData() async throws -> Data {
    sleep(2)
    let data = Data()  // 本来ならサーバーからデータ取得(成功 or 失敗)
    return data
}

print("START")

Task {
    do {
        let data = try await fetchData()
        print("Data received: \(data)")
    } catch {
        print("Error: \(error)")
    }
}

あえてコードを長ったらしく書いた部分もありますが、コード量を抑えられて見通しが良くなったと思います。ではそれぞれの使い方を見ていきます。

asyncキーワード

asyncキーワードは「非同期」の意味を持つ「Asynchronous:アシンクロナス」から来ています。このキーワードはthrowsキーワードと同様にパラメータの後に記述します。throwsキーワードも付与する場合はasync throwsの順に記述します。

asyncキーワードのついた関数(メソッド)は非同期関数(メソッド)とみなされます。これはこの関数が非同期的に実行され、実行途中で中断される可能性を孕んだ特殊な種類の関数であることを示しています。

func fetchData() async throws -> Data { }

そして非同期関数(メソッド)を呼び出すためにはawaitキーワードが必要になります。

awaitキーワード

awaitキーワードは非同期関数(メソッド)を呼び出していることをマークするためのキーワードです。このマークが付与されていると呼び出された非同期関数が完了する(返される)まで実行が一時停止されます(つまり直列処理になる)。そのため例えば以下のような場合は1が出力されてから2が出力されるのはfetchDataが完了した後の2秒後になります。

print("1")
let data = await fetchData() // 2秒かかる処理
print("2")

asyncのついた関数をawaitつけずに呼び出そうとすると以下のようなエラーになります。

Expression is 'async' but is not marked with 'await'

またasyncがついた非同期関数をさらに別の関数から呼びだす場合はその関数にもasyncをつける必要があります。

func parentFunction() async {
  print("1")
  let data = await fetchData() // 2秒かかる処理
  print("2") // fetchDataが完了した2秒後に呼び出される
}

つけ忘れると以下のエラーになります。

'async' call in a function that does not support concurrency

Task構造体

公式リファレンス:Task構造体

最終的に非同期関数を呼び出すにはTask構造体にクロージャーとして渡す必要があります。先ほどの'async' call in a function that does not support concurrencyエラーが出ている場合にはasyncを付与するかTaskに渡すことで解決できます。

Task非同期作業の単位であり、非同期処理は1つのタスクの中で処理されます。Task.initの定義にはクロージャーを受け取り、そのクロージャーにはasyncが付いています。

init(priority: TaskPriority?, operation: () async -> Success)

そのためTrailing Closure記法で省略してTask{}と記述できます。

Task {
  print("1")
  let data = await fetchData() // 2秒かかる処理
  print("2")
}

タスクインスタンスを作成後、クロージャーに渡した処理は即座に実行され、またこのインスタンスを使用してタスクの操作を行うことができるようになります。

まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。

ご覧いただきありがとうございました。

searchbox

スポンサー

ProFile

ame

趣味:読書,プログラミング学習,サイト制作,ブログ

IT嫌いを克服するためにITパスを取得しようと勉強してからサイト制作が趣味に変わりました笑
今はCMSを使わずこのサイトを完全自作でサイト運営中〜

New Article

index