Wprowadzenie do Futures i Promises
Poznaj podstawowe koncepcje `Future` reprezentującego wyniki asynchroniczne oraz `Promise` służącego do ich realizacji.
Wprowadzenie do Futures i Promises to bezpłatna lekcja Scala for Backend Engineering & Functional Programming na CoddyKit. To lekcja 1 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.
Kod asynchroniczny: dlaczego ma znaczenie
W programowaniu zadania mogą działać synchronicznie (jedno po drugim) albo asynchronicznie (zadania mogą się rozpoczynać bez oczekiwania na zakończenie poprzednich).
Programowanie asynchroniczne ma kluczowe znaczenie przy tworzeniu responsywnych aplikacji, które nie zawieszają się podczas oczekiwania na powolne operacje, takie jak żądania sieciowe lub zapytania do bazy danych.
Problem z operacjami blokującymi
Wyobraź sobie, że program musi pobrać dane z Internetu. Jeśli ta operacja jest blokująca, program zatrzyma się i nie zrobi nic więcej, dopóki nie otrzyma danych.
Może to prowadzić do „zamrożenia” interfejsu użytkownika albo do nieefektywnego działania aplikacji po stronie serwera. Potrzebujemy sposobu na wykonywanie tych zadań w tle bez zatrzymywania głównego toku działania.
Wprowadzenie do Scala Futures
Scala Future to zaawansowane narzędzie do programowania asynchronicznego. Reprezentuje wartość, która może nie być jeszcze dostępna, ale pojawi się w przyszłości.
- Można myśleć o nim jak o zamówieniu kawy: otrzymuje Pan/Pani paragon (
Future) i może robić inne rzeczy, podczas gdy kawa jest przygotowywana. - Gdy kawa jest gotowa, paragon zostaje „zrealizowany” wraz z napojem (wynikiem).
Tworzenie pierwszego Future
Future można utworzyć, opakowując obliczenie w Future { ... }. Obliczenie zostanie wykonane w osobnym wątku.
Do planowania tych zadań potrzebny jest ExecutionContext. Dla uproszczenia użyjemy globalnego kontekstu.
import scala.concurrent.{Future, ExecutionContext}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
object Main extends App {
println("Starting computation...")
val f: Future[Int] = Future {
Thread.sleep(1000) // Simulate a long operation
42
}
f.onComplete {
case Success(value) => println(s"Result: $value")
case Failure(e) => println(s"Error: ${e.getMessage}")
}
println("Computation started, continuing...")
// Keep main thread alive for a bit to see Future's result
Thread.sleep(2000)
}Zrozumienie ExecutionContext
ExecutionContext to zasadniczo pula wątków, w której wykonywane są obliczenia asynchroniczne.
- Zarządza sposobem i momentem wykonywania zadań.
- W krótkich przykładach często używa się
scala.concurrent.ExecutionContext.Implicits.global, które udostępnia domyślną pulę wątków. - W rzeczywistych aplikacjach można skonfigurować konkretne pule wątków, aby lepiej kontrolować zużycie zasobów.
Obsługa wyników Future: onComplete
Metoda onComplete służy do definiowania działania po zakończeniu Future, niezależnie od tego, czy operacja zakończy się powodzeniem, czy niepowodzeniem.
Przyjmuje funkcję działającą na obiekcie scala.util.Try, który może mieć wartość Success(value) albo Failure(exception).
import scala.concurrent.{Future, ExecutionContext}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
object Main extends App {
val futureSuccess = Future { 10 / 2 }
val futureFailure = Future { 10 / 0 }
futureSuccess.onComplete {
case Success(result) => println(s"Success: $result")
case Failure(e) => println(s"Error: ${e.getMessage}")
}
futureFailure.onComplete {
case Success(result) => println(s"Success: $result")
case Failure(e) => println(s"Error: ${e.getMessage}")
}
Thread.sleep(100) // Allow Futures to complete and print
}Uproszczone procedury obsługi: Success/Failure
W prostszych przypadkach można użyć onSuccess i onFailure, aby obsługiwać odpowiednio tylko pomyślne wyniki albo tylko błędy.
onSuccessprzyjmuje funkcję dla pomyślnej wartości.onFailureprzyjmuje funkcję dla wyjątku.- Są to metody pomocnicze zbudowane na bazie
onComplete.
import scala.concurrent.{Future, ExecutionContext}
import scala.concurrent.ExecutionContext.Implicits.global
object Main extends App {
val futureValue = Future { "Hello, Scala!" }
futureValue.onSuccess { case msg => println(s"Got message: $msg") }
futureValue.onFailure { case e => println(s"Oops, an error: ${e.getMessage}") }
val futureError = Future { throw new RuntimeException("Something broke!") }
futureError.onSuccess { case msg => println(s"Got message: $msg") }
futureError.onFailure { case e => println(s"Oops, an error: ${e.getMessage}") }
Thread.sleep(100) // Allow Futures to complete and print
}Czym jest Scala Promise?
Podczas gdy Future jest przeznaczonym tylko do odczytu miejscem na wynik, Promise jest kontenerem jednokrotnego zapisu, który można zakończyć wartością albo wyjątkiem.
Promisesłuży do zakończenia obiektuFuture.- Tworzy się obiekt
Promise, pobiera powiązany z nimFuture, a następnie kończyPromise, gdy wynik jest gotowy.
Realizacja Promise
Obiekt Promise można zakończyć za pomocą jego metod success() lub failure(). Po zakończeniu Promise powiązany z nim Future również zostanie zakończony.
Próba zakończenia Promise więcej niż raz spowoduje błąd.
import scala.concurrent.{Future, Promise, ExecutionContext}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
object Main extends App {
val p = Promise[String]() // Create a Promise that will hold a String
val f: Future[String] = p.future // Get the Future associated with the Promise
f.onComplete {
case Success(value) => println(s"Future completed with: '$value'")
case Failure(e) => println(s"Future failed with: ${e.getMessage}")
}
// Simulate an async operation completing the promise
Future {
Thread.sleep(500)
p.success("Data fetched successfully!") // Complete the Promise
}
println("Promise created, waiting for completion...")
Thread.sleep(1000) // Keep main thread alive to see Future's result
}Future i Promise razem
Obiekty Future i Promise często współpracują ze sobą. Typowy wzorzec polega na użyciu Promise, gdy potrzebna jest ręczna kontrola nad zakończeniem Future, na przykład za pośrednictwem zewnętrznego systemu lub starszego API opartego na callbackach.
Pozwala to opakować asynchroniczne operacje, które nie korzystają z Future, w model Future i dzięki temu używać ich z kodem opartym na Future.
import scala.concurrent.{Future, Promise, ExecutionContext}
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
object Main extends App {
def performLongTask(data: String): Future[String] = {
val p = Promise[String]()
// Simulate an external system or callback-based API
Future {
Thread.sleep(700)
if (data == "fail") p.failure(new Exception("Task failed!"))
else p.success(s"Processed: $data")
}
p.future // Return the Future that will be completed by the Promise
}
val task1 = performLongTask("input data")
task1.onComplete { case Success(res) => println(s"Task 1: $res") case Failure(e) => println(s"Task 1: ${e.getMessage}") }
val task2 = performLongTask("fail")
task2.onComplete { case Success(res) => println(s"Task 2: $res") case Failure(e) => println(s"Task 2: ${e.getMessage}") }
Thread.sleep(1500) // Keep main thread alive for results
}Szybkie sprawdzenie: Future a Promise
Które z poniższych stwierdzeń poprawnie opisują role obiektów Future i Promise w Scali?
Podsumowanie: Futures i Promises
Świetna praca! W tej lekcji poznali Państwo podstawy programowania asynchronicznego w Scali:
Future: Symbol zastępczy wyniku, który będzie dostępny w przyszłości.Promise: Kontener jednokrotnego zapisu, używany do ręcznego zakończenia obiektuFuture.ExecutionContext: Planuje wykonanie zadań asynchronicznych.- Sposoby obsługi wyników
Futureza pomocą metodonComplete,onSuccessionFailure.
Te pojęcia stanowią podstawę bardziej złożonych wzorców asynchronicznych!
Często zadawane pytania
Czy lekcja „Wprowadzenie do Futures i Promises” jest bezpłatna?
Tak — pełny tekst „Wprowadzenie do Futures i Promises” 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 „Wprowadzenie do Futures i Promises”?
Poznaj podstawowe koncepcje `Future` reprezentującego wyniki asynchroniczne oraz `Promise` służącego do ich realizacji. Ć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 1 z 3.
Ile czasu zajmuje lekcja „Wprowadzenie do Futures i Promises”?
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
- Wprowadzenie do Futures i Promises
- Łączenie operacji asynchronicznych
- Obsługa błędów w Futures