• Keine Ergebnisse gefunden

Reaktive Programmierung Vorlesung 10 vom 02.06.15: Reactive Streams (Observables)

N/A
N/A
Protected

Academic year: 2022

Aktie "Reaktive Programmierung Vorlesung 10 vom 02.06.15: Reactive Streams (Observables)"

Copied!
34
0
0

Wird geladen.... (Jetzt Volltext ansehen)

Volltext

(1)

Reaktive Programmierung

Vorlesung 10 vom 02.06.15: Reactive Streams (Observables)

Christoph Lüth & Martin Ring

Universität Bremen

Sommersemester 2015

14:21:12 2015-06-24 1 [1]

(2)

Fahrplan

I Teil I: Grundlegende Konzepte

I Teil II: Nebenläufigkeit

I Futures and Promises

I Das Aktorenmodell

I Aktoren und Akka

I Reaktive Datenströme - Observables

I Reaktive Datenströme - Back Pressure und Spezifikation

I Reaktive Datenströme - Akka Streams

I Teil III: Fortgeschrittene Konzepte

(3)

Klassifikation von Effekten

Einer Viele

Synchron Try[T] Iterable[T]

Asynchron Future[T] Observable[T]

I Trymacht Fehler explizit

I FuturemachtVerzögerung explizit

I Explizite Fehler bei Nebenläufigkeitunverzichtbar

I Heute:Observables

3 [1]

(4)

Future[T] ist dual zu Try[T]

trait Future[T] {

def onComplete(callback: Try[T] ⇒ Unit): Unit }

I (Try[T] ⇒ Unit)⇒ Unit

I Umgedreht:

Unit ⇒ (Unit ⇒ Try[T])

I () ⇒ (()⇒ Try[T])

I ≈ Try[T]

(5)

Future[T] ist dual zu Try[T]

trait Future[T] {

def onComplete(callback: Try[T] ⇒ Unit): Unit }

I (Try[T] ⇒ Unit)⇒ Unit

I Umgedreht:

Unit ⇒ (Unit ⇒ Try[T])

I () ⇒ (()⇒ Try[T])

I ≈ Try[T]

4 [1]

(6)

Future[T] ist dual zu Try[T]

trait Future[T] {

def onComplete(callback: Try[T] ⇒ Unit): Unit }

I (Try[T] ⇒ Unit)⇒ Unit

I Umgedreht:

Unit ⇒ (Unit ⇒ Try[T])

I () ⇒ (()⇒ Try[T])

I ≈ Try[T]

(7)

Future[T] ist dual zu Try[T]

trait Future[T] {

def onComplete(callback: Try[T] ⇒ Unit): Unit }

I (Try[T] ⇒ Unit)⇒ Unit

I Umgedreht:

Unit ⇒ (Unit ⇒ Try[T])

I () ⇒ (()⇒ Try[T])

I ≈ Try[T]

4 [1]

(8)

Future[T] ist dual zu Try[T]

trait Future[T] {

def onComplete(callback: Try[T] ⇒ Unit): Unit }

I (Try[T] ⇒ Unit)⇒ Unit

I Umgedreht:

Unit ⇒ (Unit ⇒ Try[T])

I () ⇒ (()⇒ Try[T])

I ≈ Try[T]

(9)

Try vs Future

I Try[T]: Blockieren −→ Try[T]

I Future[T]: Callback−→ Try[T](Reaktiv)

5 [1]

(10)

Was ist dual zu Iterable?

trait Iterable[T] { def iterator(): Iterator[T] } trait Iterator[T] { def hasNext: Boolean

def next(): T }

I () ⇒ () ⇒ Try[Option[T]]

I Umgedreht:

(Try[Option[T]] ⇒ Unit)⇒ Unit

I ( T ⇒ Unit, Throwable ⇒ Unit, ()⇒ Unit )⇒ Unit

(11)

Was ist dual zu Iterable?

trait Iterable[T] { def iterator(): Iterator[T] } trait Iterator[T] { def hasNext: Boolean

def next(): T }

I () ⇒

() ⇒ Try[Option[T]]

I Umgedreht:

(Try[Option[T]] ⇒ Unit)⇒ Unit

I ( T ⇒ Unit, Throwable ⇒ Unit, ()⇒ Unit )⇒ Unit

6 [1]

(12)

Was ist dual zu Iterable?

trait Iterable[T] { def iterator(): Iterator[T] } trait Iterator[T] { def hasNext: Boolean

def next(): T }

I () ⇒ () ⇒ Try[Option[T]]

I Umgedreht:

(Try[Option[T]] ⇒ Unit)⇒ Unit

I ( T ⇒ Unit, Throwable ⇒ Unit, ()⇒ Unit )⇒ Unit

(13)

Was ist dual zu Iterable?

trait Iterable[T] { def iterator(): Iterator[T] } trait Iterator[T] { def hasNext: Boolean

def next(): T }

I () ⇒ () ⇒ Try[Option[T]]

I Umgedreht:

(Try[Option[T]] ⇒ Unit)⇒ Unit

I ( T ⇒ Unit, Throwable ⇒ Unit, ()⇒ Unit )⇒ Unit

6 [1]

(14)

Was ist dual zu Iterable?

trait Iterable[T] { def iterator(): Iterator[T] } trait Iterator[T] { def hasNext: Boolean

def next(): T }

I () ⇒ () ⇒ Try[Option[T]]

I Umgedreht:

(Try[Option[T]] ⇒ Unit)⇒ Unit

I ( T ⇒ Unit, Throwable ⇒ Unit, ()⇒ Unit )⇒ Unit

(15)

Observable[T] ist dual zu Iterable[T]

trait Iterable[T] { def iterator:

Iterator[T]

}

trait Iterator[T] { def hasNext: Boolean def next(): T

}

trait Observable[T] { def subscribe(Observer[T]

observer):

Subscription }

trait Observer[T] {

def onNext(T value): Unit def onError(Throwable error):

Unit

def onCompleted(): Unit }

trait Subscription { def unsubscribe(): Unit }

7 [1]

(16)

Warum Observables?

class Robot(var pos: Int, var battery: Int) { def goldAmounts = new Iterable[Int] {

def iterator = new Iterator[Int] { def hasNext = world.length > pos def next() = if (battery > 0) {

Thread.sleep(1000) battery -= 1

pos += 1

world(pos).goldAmount

} else sys.error("low battery") }

} }

(robotA.goldAmounts zip robotB.goldAmounts)

(17)

Observable Robots

class Robot(var pos: Int, var battery: Int) { def goldAmounts = Observable { obs ⇒

var continue = true

while (continue && world.length > pos) { if (battery > 0) {

Thread.sleep(1000) pos += 1

battery -= 1

obs.onNext(world(pos).gold)

} else obs.onError(new Exception("low battery")) }

obs.onCompleted()

Subscription(continue = false) }

}

(robotA.goldAmounts zip robotB.goldAmounts)

.map(_ + _).takeUntil(_ > 5) 9 [1]

(18)

Observables Intern

DEMO

(19)

Observable Contract

I dieonNextMethode eines Observers wird beliebig oft aufgerufen.

I onCompletedoder onErrorwerden nur einmal aufgerufen und schließen sich gegenseitig aus.

I NachdemonCompletedoderonError aufgerufen wurde wirdonNext nicht mehr aufgerufen.

onNext*(onCompleted|onError)?

I Diese Spezifikation wird durch die Konstruktoren erzwungen.

11 [1]

(20)

map

def map[U](f: T ⇒ U): Observable[U]

(21)

flatMap

def flatMap[U](f: T ⇒ Observable[U]): Observable[U]

13 [1]

(22)

filter

def filter(f: T ⇒ Boolean): Observable[T]

(23)

take

def take(count: Int): Observable[T]

15 [1]

(24)

last

def last: Observable[T]

(25)

groupBy

def groupBy[U](T ⇒ U): Observable[Observable[T]]

17 [1]

(26)

window

def window(count: Int): Observable[Observable[T]]

(27)

merge

def merge[T](obss: Observable[T]*): Observable[T]

19 [1]

(28)

zip

def zip[U,S](obs: Observable[U], f: (T,U) ⇒ S):

Observable[S]

(29)

switch

def switch(): Observable[T]

21 [1]

(30)

Subscriptions

I Subscriptions können mehrfach gecancelt werden. Deswegen müssen sie idempotent sein.

Subscription(cancel: ⇒ Unit)

BooleanSubscription(cancel: ⇒ Unit) class MultiAssignmentSubscription {

def subscription_=(s: Subscription) def subscription: Subscription }

CompositeSubscription(subscriptions: Subscription*)

(31)

Schedulers

I Nebenläufigkeit überScheduler trait Scheduler {

def schedule(work: ⇒ Unit): Subscription }

trait Observable[T] { ...

def observeOn(schedule: Scheduler): Observable[T]

}

I Subscription.cancel()muss synchronisiert sein.

23 [1]

(32)

Hot vs. Cold Streams

I Hot Observablesschicken allen Observern die gleichen Werte zu den gleichen Zeitpunkten.

z.B. Maus Klicks

I Cold Observablesfangen erst an Werte zu produzieren, wenn man ihnen zuhört. Für jeden Observer von vorne.

z.B.Observable.from(Seq(1,2,3))

(33)

Observables Bibliotheken

I Observables sind eine Idee von Eric Meijer

I Bei Microsoft als .netReactive Extension (Rx) enstanden

I Viele Implementierungen für verschiedene Platformen

I RxJava, RxScala, RxClosure (Netflix)

I RxPY, RxJS, ... (ReactiveX)

I Vorteil: Elegante Abstraktion, Performant

I Nachteil: Push-Modell ohne Bedarfsrückkopplung

25 [1]

(34)

Zusammenfassung

I Futures sind dual zu Try

I Observables sind dual zu Iterable

I Observables abstrahieren viele Nebenläufigkeitsprobleme weg:

Außenfunktional (Hui) - Innen imperativ(Pfui)

I Nächstes mal:Back Pressureund noch mehr reaktive Ströme

Referenzen

ÄHNLICHE DOKUMENTE

I Dynamische Tests führen das Programm unter kontrollierten Bedingungen aus, und prüfen das Ergebnis gegen eine gegebene Spezifikation. I Zentrale Frage: wo kommen die

I Systeme sind eingebettet, nebenläufig, reagieren auf ihre Umwelt.... Warum

I Promises sind das Gegenstück zu Futures trait Promise {. def complete(result: Try[T]) def

I Werte vom Typ IO (Aktionen) können kombiniert werden wie alle anderen. I

I Aber: zentrales Konzept sind unendliche Listen (Ströme) mit nicht-strikte Auswertung. I Implementation mit Scala-Listen

Subscriber prior to any other signals to that Subscriber and MUST return normally, except when the provided Subscriber is null in which case it MUST throw

Quelle (Source) – Verarbeitungsschritt mit genau einem Ausgang Senke (Sink) – Verarbeitungsschritt mit genau einem Eingang.. Datenfluss (Flow) – Verarbeitungsschritt mit jeweils

I Nachdem onCompleted oder onError aufgerufen wurde wird onNext nicht mehr