0Pricing
Scala for Backend Engineering & Functional Programming · Lekcja

Łączenie operacji asynchronicznych

Naucz się łączyć i komponować wiele obiektów `Future` za pomocą metod takich jak `map`, `flatMap` i wyrażeń `for`.

Łączenie operacji asynchronicznych to bezpłatna lekcja Scala for Backend Engineering & Functional Programming na CoddyKit. To lekcja 2 z 3. Możesz przeczytać całą lekcję poniżej za darmo — a potem ćwiczyć ją interaktywnie w przeglądarce z wbudowanym edytorem kodu i tutorem AI dostępnym 24/7. To część ścieżki edukacyjnej Scala for Backend Engineering & Functional Programming, a Twój postęp synchronizuje się między webem a aplikacją CoddyKit. Kurs Scala for Backend Engineering & Functional Programming zawiera 3 lekcji w sumie.

Dlaczego łączyć operacje asynchroniczne?

Obiekty Future pozwalają uruchamiać zadania bez blokowania głównego programu. Co jednak zrobić, gdy wynik jednego zadania jest potrzebny innemu albo trzeba połączyć wyniki wielu zadań?

W takich sytuacjach przydaje się komponowanie obiektów Future. Nauczą się Państwo łączyć te operacje asynchroniczne w łańcuchy oraz scalać je, aby kod Scali był bardziej wydajny i responsywny.

`map`: przekształcanie wyniku obiektu Future

Metody map używa się, gdy mają Państwo obiekt Future i chcą przekształcić jego pomyślny wynik w inną wartość. Podana funkcja przekształcająca zostanie zastosowana po pomyślnym zakończeniu obiektu Future.

  • Zawsze zwraca nowy obiekt Future.
  • Doskonale nadaje się do prostych, synchronicznych przekształceń wyniku asynchronicznego.

`map` w działaniu (kod)

W tym przykładzie pobieramy liczbę z obiektu Future, a następnie podwajamy ją za pomocą map. Przekształcenie następuje dopiero po zakończeniu początkowego obiektu 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`: łączenie obiektów Future

Czasami pomyślny wynik jednego obiektu Future musi uruchomić kolejny obiekt Future. Właśnie w tym sprawdza się flatMap. „Spłaszcza” obiekt Future zawierający inny obiekt Future (Future[Future[T]]) do pojedynczego obiektu Future[T].

  • Należy go używać, gdy funkcja przekształcająca zwraca obiekt Future.
  • Jest niezbędny w sekwencyjnych operacjach asynchronicznych.

`flatMap` w działaniu (kod)

Pobieramy identyfikator użytkownika, a następnie używamy go do pobrania danych użytkownika. Każdy etap jest operacją asynchroniczną zwracającą obiekt Future. flatMap zapewnia wykonanie ich w odpowiedniej kolejności.

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` a `flatMap`: najważniejsza różnica

Często zdarza się mylić map z flatMap. Należy pamiętać:

  • map: Państwa funkcja zwraca zwykłą wartość (np. Int, String). Future[A].map(A => B) daje w wyniku Future[B].
  • flatMap: Państwa funkcja zwraca kolejny obiekt Future (np. Future[Int], Future[String]). Future[A].flatMap(A => Future[B]) daje w wyniku Future[B].

flatMap służy do łączenia operacji asynchronicznych, natomiast map — do synchronicznego przekształcania wyniku asynchronicznego.

Wyrażenia `for`: eleganckie łączenie

Wyrażenia for w Scali zapewniają przejrzystą, czytelną składnię do łączenia operacji wykorzystujących map i flatMap. Są wyłącznie cukrem składniowym, co oznacza, że kompilator tłumaczy je na serię wywołań map, flatMap i filter.

Gdy mają Państwo wiele zależnych od siebie operacji Future, wyrażenia for sprawiają, że kod wygląda niemal jak synchroniczny.

Kod wyrażenia `for`

Ten przykład wykorzystuje wyrażenie for do połączenia tej samej logiki pobierania identyfikatora użytkownika i jego danych, którą wcześniej przedstawiliśmy za pomocą flatMap. Proszę zauważyć, o ile kod jest czytelniejszy!

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`: łączenie niezależnych wyników

Co zrobić, gdy mają Państwo dwa niezależne obiekty Future i chcą połączyć ich wyniki po zakończeniu obu operacji? Metoda zip doskonale się do tego nadaje. Przyjmuje ona kolejny obiekt Future i zwraca nowy obiekt Future zawierający parę (Tuple) ich pomyślnych wyników.

Oba obiekty Future są wykonywane współbieżnie, a zip czeka na zakończenie obu.

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!")
  }
}

Szybkie sprawdzenie: komponowanie obiektów Future

Proszę rozważyć poniższy fragment kodu Scali. Jaka będzie końcowa wypisana wartość?

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)
  }
}

Podsumowanie: łączenie zadań asynchronicznych

Poznali Państwo skuteczne sposoby komponowania obiektów Future w Scali:

  • map: Przekształca pomyślny wynik obiektu Future w nową wartość.
  • flatMap: Łączy dwa obiekty Future, wykorzystując wynik pierwszego do uruchomienia drugiego.
  • Wyrażenia for: Zapewniają przejrzystą, przypominającą synchroniczną składnię dla łańcuchów flatMap i map.
  • zip: Łączy wyniki dwóch niezależnych obiektów Future w krotkę.

Te narzędzia mają kluczowe znaczenie przy tworzeniu responsywnych i wydajnych aplikacji asynchronicznych w Scali!

Często zadawane pytania

Czy lekcja „Łączenie operacji asynchronicznych” jest bezpłatna?

Tak — pełny tekst „Łączenie operacji asynchronicznych” jest dostępny za darmo tutaj w sieci. Aby ćwiczyć ją interaktywnie (wbudowany edytor kodu i tutor AI dostępny 24/7) i odblokować resztę kursu Scala for Backend Engineering & Functional Programming, przejdź na CoddyKit PRO. Kurs Scala for Backend Engineering & Functional Programming zawiera 3 lekcji w sumie.

Co nauczysz się w „Łączenie operacji asynchronicznych”?

Naucz się łączyć i komponować wiele obiektów `Future` za pomocą metod takich jak `map`, `flatMap` i wyrażeń `for`. Ćwiczysz Scala for Backend Engineering & Functional Programming z praktycznym kodem, który uruchamiasz bezpośrednio w przeglądarce, a tutor AI dostępny 24/7 odpowiada na Twoje pytania podczas pracy nad lekcją.

Czy potrzebuję doświadczenia, aby zacząć Scala for Backend Engineering & Functional Programming?

Nie wymagamy żadnego doświadczenia. Scala for Backend Engineering & Functional Programming w CoddyKit jest strukturyzowany dla początkujących i zaawansowanych użytkowników, więc możesz zacząć tutaj lub od początku i uczyć się w swoim tempie. To lekcja 2 z 3.

Ile czasu zajmuje lekcja „Łączenie operacji asynchronicznych”?

Większość lekcji CoddyKit trwa około 5–10 minut. Każda lekcja to mały, interaktywny krok, dzięki czemu robisz systematyczne postępy i zawsze wracasz dokładnie do tego samego miejsca — na webie i w aplikacji.

Czy mogę pisać i uruchamiać kod w tej lekcji Scala for Backend Engineering & Functional Programming?

Tak. Każda lekcja Scala for Backend Engineering & Functional Programming zawiera wbudowany edytor kodu, więc piszesz i uruchamiasz prawdziwy kod bezpośrednio w przeglądarce i od razu otrzymujesz sprzężenie zwrotne od AI — bez konfiguracji na komputerze.

Wszystkie lekcje w tym kursie

  1. Wprowadzenie do Futures i Promises
  2. Łączenie operacji asynchronicznych
  3. Obsługa błędów w Futures
← Powrót do Scala for Backend Engineering & Functional Programming