Scala.Rx的动机和基本使用方法及内部实现方式,原理简单明了的介绍清楚了。

献良发布于2018/10/17

注脚

展开查看详情

1. Scala.Rx Scaladays 2014, Berlin Li Haoyi https://github.com/lihaoyi/scala.rx

2.What ● libraryDependencies += "com.scalarx" %% "scalarx" % "0.2.5" ● Scala.Rx is a change-propagation library ● Reactive values which depend on each other ● Change one and they propagate the update

3.Reactive values which depend on each other

4.Change one and they propagate the update

5.Motivation var a = 1; var b = 2 val c = a + b println(c) // 3 a = 4 println(c) // 3

6.Motivation var a = 1; var b = 2 def c = a + b println(c) // 3 a = 4 println(c) // 6

7.Motivation var a = 1; var b = 2 def c = veryExpensiveOperation(a, b) println(c) // 3 a = 4 println(c) // 6

8.Motivation var a = 1; var b = 2 def c = a + b // onChange(c, () => ...) a = 4

9.Motivation import rx._ val a = Var(1); val b = Var(2) val c = Rx{ a() + b() } println(c()) // 3 a() = 4 println(c()) // 6

10.Motivation import rx._ val a = Var(1); val b = Var(2) val c = Rx{ a() + b() } println(c()) // 3 a() = 4 println(c()) // 6 Obs(c){ ... do something... }

11.What ● Var: reactive variables that are set manually ● Rx: reactive values that depend on other reactive values ● Obs: observes changes to reactive values and does things

12.Why ● Most mutable state isn’t really “state” ○ Depends on other variables ○ Should be kept in sync ○ Weird things happen if it falls out of sync? ● When recalculating something, you want to do it the same way you did it the first time ● Scala.Rx saves you from having to keep things in sync manually

13.What - Observers val a = Var(1) var count = 0 val o = Obs(a){ count = a() + 1 } println(count) // 2 a() = 4 println(count) // 5

14.What - Propagation val a = Var(1) // 1 val b = Var(2) // 2 val c = Rx{ a() + b() } // 3 val d = Rx{ c() * 5 } // 15 val e = Rx{ c() + 4 } // 7 val f = Rx{ d() + e() + 4 } // 26 println(f()) // 26 a() = 3 println(f()) // 38

15.Exceptions val a = Var(1L) val b = Var(2L) val c = Rx{ a() / b() } val d = Rx{ a() * 5 } val e = Rx{ 5 / b() } val f = Rx{ a() + b() + 2 } val g = Rx{ f() + c() } b() = 0 // uh oh

16.Console Demo

17.Scala.js Demo

18.Exceptions Demo

19.Scala.js Demo 2

20.How val a = Rx{b() + c()} ● Rx.apply pushes itself onto a thread-local stack before evaluating contents ● b.apply, c.apply look at who’s on top of the stack and add the dependency

21.Propagation Strategy ● Controlled by a Propagator ● When call Var.update, how/when do its dependencies update?

22.Propagation Strategy ● Propagator.Immediate: happens on current thread, finishes before .update returns ● Propagator.ExecContext: happens on whatever ExecutionContext is given, . update returns a Future[Unit] ● Both happen in roughly-breadth-first, topological order.

23.Topological Order 1 2 3 4

24.Overall Characteristics ● Dependency graph constructed at runtime ○ No need to live in a monad ○ No need to specify what the dependencies are ● No globals, only one thread-local stack ○ Easy to use as one part of a larger program. ○ Small fragments of change-propagation in a larger non-Scala.Rx world ○ Easily interops with non-Scala.Rx world

25.Limitations ● Dependency graph can change shape ○ Rxs may evaluate out of order ○ Rxs may evaluate more than once ● Thread local stack doesn’t play nicely with Futures ● Rx initialization is blocking ○ Can’t initialize more than one in parallel

26.Limitations val a = Var(1) // depth 0 val b = Rx{ a() + 1 } // depth 1 val c = Rx{ // depth 1 or 2??? if (random() > 0.5) b() + 1 else a() + 1 }

27.Limitations val a = Rx{ ... } val b = Rx{ Future(a()) }

28.Limitations import concurrent.ExecutionContext.global implicit val prop = { new Propagator.ExecContext()(global) } val a = Var(1) val b = Rx{ expensiveCompute(a() + 1) } val c = Rx{ expensiveCompute(a() + 2) }

29.Scope ● Useless in stateless web services ● Useless in pure-functional code ● Doesn’t support a rich event-stream API ● Doesn’t support channels, coroutines, async