0Pricing
Scala for Backend Engineering & Functional Programming · レッスン

非同期処理の組み立て

`map`、`flatMap`、`for`内包表記などのメソッドを使って、複数の `Future` を連結・結合する方法を学びます。

「非同期処理の組み立て」はCoddyKit上の無料Scala for Backend Engineering & Functional Programmingレッスンです。 これはレッスン2/3です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはScala for Backend Engineering & Functional Programming学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Scala for Backend Engineering & Functional Programmingコースには全3レッスンが含まれています。

非同期処理を連鎖させる理由

Futureを使うと、メインプログラムをブロックせずにタスクを実行できます。しかし、あるタスクの結果を別のタスクで使用する必要がある場合や、複数のタスクの結果を組み合わせる必要がある場合はどうすればよいでしょうか。

このようなときに役立つのがFutureの合成です。非同期処理を連鎖させたり組み合わせたりする方法を学び、Scalaのコードをより強力で応答性の高いものにしていきます。

`map`: Futureの結果を変換する

mapメソッドは、Futureがあり、その成功した結果を別の値に変換したい場合に使用します。指定した変換関数は、Futureが正常に完了した後に適用されます。

  • 常に新しいFutureを返します。
  • 非同期の結果に対する単純な同期変換に適しています。

`map`の使用例(コード)

ここでは、Futureから数値を取得し、mapを使って2倍にしています。変換は、最初のFutureが完了した後にのみ行われます。

import scala.concurrent.{Future, Await}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._

object Main {
  def main(args: Array[String]): Unit = {
    val originalFuture: Future[Int] = Future {
      Thread.sleep(100) // Simulate work
      10
    }

    val doubledFuture: Future[Int] = originalFuture.map(value => value * 2)

    val result = Await.result(doubledFuture, 1.second)
    println(s"Doubled value: $result")
  }
}

`flatMap`: Futureを連鎖させる

あるFutureの成功した結果を使って、別のFutureを開始したい場合があります。このようなときに力を発揮するのがflatMapです。FutureのFuture(Future[Future[T]])を、1つのFuture[T]に「平坦化」します。

  • 変換関数がFutureを返す場合に使用します。
  • 非同期処理を順番に実行するために不可欠です。

`flatMap`の使用例(コード)

ユーザーIDを取得し、そのIDを使ってユーザーの詳細を取得します。各ステップはFutureを返す非同期処理です。flatMapによって、これらの処理が順番に実行されます。

import scala.concurrent.{Future, Await}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._

object Main {
  def main(args: Array[String]): Unit = {
    def fetchUserId(): Future[Int] = Future {
      Thread.sleep(50)
      123
    }

    def fetchUserDetails(userId: Int): Future[String] = Future {
      Thread.sleep(100)
      s"User $userId details"
    }

    val userDetailsFuture: Future[String] = fetchUserId().flatMap { userId =>
      fetchUserDetails(userId)
    }

    val result = Await.result(userDetailsFuture, 1.second)
    println(s"Details: $result")
  }
}

`map`と`flatMap`の違い:重要なポイント

mapとflatMapを混同することはよくあります。次のように覚えておいてください。

  • map:関数が通常の値(例:Int、String)を返します。Future[A].map(A => B)の結果はFuture[B]です。
  • flatMap:関数が別のFuture(例:Future[Int]、Future[String])を返します。Future[A].flatMap(A => Future[B])の結果はFuture[B]です。

flatMapは非同期処理を連鎖させるために使い、mapは非同期の結果を同期的に変換するために使います。

`for`内包表記:エレガントな連鎖

Scalaのfor内包表記は、mapやflatMapを使う処理を連鎖させるための、簡潔で読みやすい構文を提供します。これは純粋な糖衣構文であり、コンパイラーによって一連のmap、flatMap、filter呼び出しに変換されます。

依存関係のあるFuture処理が複数ある場合、for内包表記を使うと、コードをほぼ同期処理のように見せることができます。

`for`内包表記のコード

この例では、for内包表記を使って、flatMapで見たものと同じユーザーID取得処理と詳細取得処理を連鎖させています。コードがずっとすっきりしていることに注目してください。

import scala.concurrent.{Future, Await}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._

object Main {
  def main(args: Array[String]): Unit = {
    def fetchUserId(): Future[Int] = Future {
      Thread.sleep(50)
      123
    }

    def fetchUserName(userId: Int): Future[String] = Future {
      Thread.sleep(100)
      s"Alice (ID: $userId)"
    }

    val userFuture: Future[String] = for {
      id <- fetchUserId()
      name <- fetchUserName(id)
    } yield name

    val result = Await.result(userFuture, 1.second)
    println(s"User name: $result")
  }
}

`zip`: 独立した結果を組み合わせる

独立した2つのFutureがあり、両方が完了した後に結果を組み合わせたい場合はどうすればよいでしょうか。この場合はzipメソッドが最適です。別のFutureを受け取り、両方の成功結果をペア(Tuple)として保持する新しいFutureを返します。

両方のFutureは並行して実行され、zipは両方の完了を待ちます。

import scala.concurrent.{Future, Await}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._

object Main {
  def main(args: Array[String]): Unit = {
    val futureA = Future {
      Thread.sleep(100)
      "Hello"
    }

    val futureB = Future {
      Thread.sleep(50)
      "World"
    }

    val combinedFuture: Future[(String, String)] = futureA.zip(futureB)

    val (greeting, subject) = Await.result(combinedFuture, 1.second)
    println(s"$greeting $subject!")
  }
}

理解度チェック:Futureの合成

次のScalaコードを見てください。最終的に何が出力されるでしょうか。

import scala.concurrent.{Future, Await}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._

object Main {
  def main(args: Array[String]): Unit = {
    val f1 = Future { 5 }
    val f2 = f1.map(x => x + 2)
    val f3 = f2.flatMap(y => Future { y * 3 })

    val result = Await.result(f3, 1.second)
    println(result)
  }
}

まとめ:非同期タスクの連鎖

ScalaのFutureを合成する強力な方法を学びました。

  • map:Futureの成功結果を新しい値に変換します。
  • flatMap:最初のFutureの結果を使って2つ目のFutureを開始し、2つのFutureを連鎖させます。
  • for内包表記:flatMapとmapの連鎖を、同期処理のように見えるすっきりした構文で記述できます。
  • zip:独立した2つのFutureの結果をタプルにまとめます。

これらのツールは、応答性と効率に優れたScalaの非同期アプリケーションを構築するうえで不可欠です。

よくある質問

「非同期処理の組み立て」レッスンは無料ですか?

はい。「非同期処理の組み立て」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Scala for Backend Engineering & Functional Programmingコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Scala for Backend Engineering & Functional Programmingコースには全3レッスンが含まれています。

「非同期処理の組み立て」で何を学びますか?

`map`、`flatMap`、`for`内包表記などのメソッドを使って、複数の `Future` を連結・結合する方法を学びます。 ブラウザで直接実行するハンズオンコードでScala for Backend Engineering & Functional Programmingを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Scala for Backend Engineering & Functional Programmingを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのScala for Backend Engineering & Functional Programmingは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン2/3です。

「非同期処理の組み立て」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このScala for Backend Engineering & Functional Programmingレッスンでコードを書いて実行できますか?

はい。すべてのScala for Backend Engineering & Functional Programmingレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. FutureとPromiseの基礎
  2. 非同期処理の組み立て
  3. Futureでのエラー処理
← Scala for Backend Engineering & Functional Programmingに戻る