0Pricing
Scala for Backend Engineering & Functional Programming · Урок

Композиция асинхронных операций

Научитесь объединять несколько `Future` в цепочки с помощью методов `map`, `flatMap` и конструкций `for`.

«Композиция асинхронных операций» — бесплатный урок Scala for Backend Engineering & Functional Programming на CoddyKit. Это урок 2 из 3. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения 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. Преобразование выполняется только после завершения исходного 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]]), в единый объект Future[T].

  • Используйте его, когда функция преобразования возвращает объект Future.
  • Он необходим для последовательного выполнения асинхронных операций.

`flatMap` в действии (код)

Мы получаем идентификатор пользователя, а затем используем его для получения сведений о пользователе. Каждый шаг является асинхронной операцией, возвращающей объект 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`: элегантное объединение в цепочку

Выражения for в Scala предоставляют понятный и удобный синтаксис для объединения в цепочку операций, использующих map и flatMap. Это всего лишь синтаксический сахар: компилятор преобразует такие выражения в последовательность вызовов map, flatMap и filter.

Если у Вас есть несколько зависимых операций с Future, выражения for делают код почти похожим на синхронный.

Код выражения `for`

В этом примере выражение for используется для объединения в цепочку той же логики получения идентификатора пользователя и сведений о нём, которую мы рассматривали с 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 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`: объединение независимых результатов

Что делать, если у Вас есть два независимых объекта Future и Вы хотите объединить их результаты после завершения обоих? Для этого отлично подходит метод zip. Он принимает другой объект Future и возвращает новый объект Future, содержащий пару (объект Tuple) их успешных результатов.

Оба объекта 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)
  }
}

Повторение: объединение асинхронных задач в цепочку

Вы изучили эффективные способы компоновать объекты Future в Scala:

  • map: преобразует успешный результат Future в новое значение.
  • flatMap: объединяет два объекта Future в цепочку, используя результат первого для запуска второго.
  • выражения for: предоставляют понятный синтаксис, похожий на синхронный, для цепочек flatMap и map.
  • zip: объединяет результаты двух независимых объектов Future в кортеж.

Эти инструменты необходимы для создания отзывчивых и эффективных асинхронных приложений на Scala!

Часто задаваемые вопросы

Урок «Композиция асинхронных операций» бесплатный?

Да — полный текст урока «Композиция асинхронных операций» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Scala for Backend Engineering & Functional Programming, подпишись на CoddyKit PRO. Курс Scala for Backend Engineering & Functional Programming содержит 3 уроков всего.

Чему я научусь в уроке «Композиция асинхронных операций»?

Научитесь объединять несколько `Future` в цепочки с помощью методов `map`, `flatMap` и конструкций `for`. Ты практикуешь Scala for Backend Engineering & Functional Programming с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Scala for Backend Engineering & Functional Programming?

Предыдущий опыт не требуется. Scala for Backend Engineering & Functional Programming на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 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