17
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【Swift Concurrency】async/awaitを使ってみよう

Posted at

はじめに

今回はasync/awaitの基本的な使い方について備忘録てきに記事にしてみます。

Swift Concurrencyって?

Swift ConcurrencyはiOS15から導入されました。async/awaitもSwiftConcurrencyの一部です。
async/awaitの他にもデータ競合問題を解消するためのActorなども導入されました。

async/awaitのいいところ

「非同期処理を同期的に書ける」

この点について紹介できたらなと思っています。
今回紹介するものがasync/awaitの全てではないので、気になったらぜひ調べてみてください。

いつ使うの?

今回はこんなシチュエーションで考えてみます。

想定シチュエーション

  • サーバーにリクエストをする
  • そのリクエストが返ってきた後に、また別のリクエストをする

Closureを使った実装方法

サーバーとのやりとりをまとめたClass

  • おそらく馴染みは深いと思うので、サクッとコードだけ載せておきます。
class ServerTaskWithoutAsyncAwait {

    // レスポンスが帰ってきた後に、別のリクエストを呼ぶとここが非常に読みにくくなる。(ネスト深い)
    func executeBothTask(completion: @escaping (Int) -> Void) {
        task1 { [weak self] num1 in
            self?.task2 { num2 in
                completion(num1 + num2)
            }
        }
    }

    private func task1(completion: @escaping (Int) -> Void) {
        sleep(1)
        print("タスク1完了")
        completion(2)
    }

    private func task2(completion: @escaping (Int) -> Void) {
        sleep(2)
        print("タスク2完了")
        completion(3)
    }
}

実行処理

    @IBAction private func tappedWithoutAsyncAwaitButton(_ sender: UIButton) {
        print("closure")
        serverTaskWithoutAsyncAwait.executeBothTask { total in
            print("total: \(total)")
        }
    }

実行結果

タスク1完了 // 実行の1秒後に表示
タスク2完了 // さらに2秒後に表示
total: 5

async/awaitを使った方法

とりあえず、使い方をざっと記します。

非同期の処理を含む関数

  • 非同期の処理を行う関数にはasyncキーワードを付与します。
  • 戻り値も普通にかけます。(普通にという表現が気持ち悪いですが、まぁ伝わるでしょう。)
  private func task1() async -> Int {
        sleep(1)
        print("タスク1完了")
        return 3
    }

    private func task2() async -> Int {
        sleep(2)
        print("タスク2完了")
        return 1
    }

asyncをつけた関数の呼び出し

  • asyncをつけた関数を呼び出すには、awaitをつけます。
  • そして、awaitを使ってる関数には、asyncを付与する必要があります
  • 今回の例で言うとexecuteBothTask()にもawaitをつける必要があります。
    func executeBothTask() async -> Int {
        let num1 = await task1()
        let num2 = await task2()
        return num1 + num2
    }

Class全体はこんな感じ

class ServerTaskWithAsyncAwait {

    private func task1() async -> Int {
        sleep(1)
        print("タスク1完了")
        return 3
    }

    private func task2() async -> Int {
        sleep(2)
        print("タスク2完了")
        return 1
    }

    func executeBothTask() async -> Int {
        let num1 = await task1()
        let num2 = await task2()
        return num1 + num2
    }
}

呼び出し

  • ボタンタップをしたときに呼び出すことを想定します
  • executeBothTask()にはasyncが付与されているので、tappedWithAsyncAwaitButtonにもasyncをつけろと言われます
  • ただ、言われた通りasyncをつけるとそれもダメといわれます。
  • その時は、以下のようにTaskで囲ってあげることで正常に動いてくれます
    @IBAction private func tappedWithAsyncAwaitButton(_ sender: UIButton) {
        print("async/await")
        Task {
            let total = await serverTaskWithAsyncAwait.executeBothTask()
            print("total: \(total)")
        }
    }

実行結果

async/await
タスク1完了 // 実行1秒後に表示
タスク2完了 // さらに2秒後に表示
total: 4

closureとasync/awaitの比較

以下の2つのコードを見比べてもらうとわかると思いますが、async/awaitを使った方が遥かにすっきりしているように見えるでしょう。
awaitを使うとその処理が終わるまで、進まずその行で待っていてくれます。処理が進まずそこで待っていてくれる。ということこそが非同期処理を同期的に書けているということです。
同期的に書くことができるので、ネストが深くならずに書けるんですね。

closure

    func executeBothTask(completion: @escaping (Int) -> Void) {
        task1 { [weak self] num1 in
            self?.task2 { num2 in
                completion(num1 + num2)
            }
        }
    }

async/await

  func executeBothTask() async -> Int {
        let num1 = await task1()
        let num2 = await task2()
        return num1 + num2
    }

おわりに

かなり雑に紹介しましたが、どうだったでしょうか?
Swift Concurrencyももっと勉強しまーす。
今回のサンプルはgithubにあるのでよければどうぞ!
ではでは!

サンプル
https://github.com/Rin-t/PracticeAsyncAwait

17
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?