-
-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
4 changed files
with
1,131 additions
and
0 deletions.
There are no files selected for viewing
53 changes: 53 additions & 0 deletions
53
src/main/scala/com/raquo/airstream/state/LazyDerivedVar.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
package com.raquo.airstream.state | ||
|
||
import com.raquo.airstream.core.AirstreamError.VarError | ||
import com.raquo.airstream.core.{AirstreamError, Transaction} | ||
|
||
import scala.util.{Failure, Success, Try} | ||
|
||
/** LazyDerivedVar has the same Var contract as DerivedVar, | ||
* but it only computes its value lazily, e.g. when you | ||
* ask for it with .now(), or when its signal has subscribers. | ||
* | ||
* Unlike the regular DerivedVar, you don't need to provide an Owner | ||
* to create LazyDerivedVar, and you're allowed to update this Var | ||
* even if its signal has no subscribers. | ||
*/ | ||
class LazyDerivedVar[A, B]( | ||
parent: Var[A], | ||
zoomIn: A => B, | ||
zoomOut: (A, B) => A, | ||
displayNameSuffix: String | ||
) extends Var[B] { | ||
|
||
override private[state] def underlyingVar: SourceVar[_] = parent.underlyingVar | ||
|
||
private[this] val _varSignal = new LazyDerivedVarSignal(parent, zoomIn, displayName) | ||
|
||
// #Note this getCurrentValue implementation is different from SourceVar | ||
// - SourceVar's getCurrentValue looks at an internal currentValue variable | ||
// - That currentValue gets updated immediately before the signal (in an already existing transaction) | ||
// - I hope this doesn't introduce weird transaction related timing glitches | ||
// - But even if it does, I think keeping derived var's current value consistent with its signal value | ||
// is more important, otherwise it would be madness if the derived var was accessed after its owner | ||
// was killed | ||
override private[state] def getCurrentValue: Try[B] = signal.tryNow() | ||
|
||
override private[state] def setCurrentValue(value: Try[B], transaction: Transaction): Unit = { | ||
parent.tryNow() match { | ||
case Success(parentValue) => | ||
// This can update the parent without causing an infinite loop because | ||
// the parent updates this derived var's signal, it does not call | ||
// setCurrentValue on this var directly. | ||
val nextValue = value.map(zoomOut(parentValue, _)) | ||
parent.setCurrentValue(nextValue, transaction) | ||
|
||
case Failure(err) => | ||
AirstreamError.sendUnhandledError(VarError(s"Unable to zoom out of lazy derived var when the parent var is failed.", cause = Some(err))) | ||
} | ||
} | ||
|
||
override val signal: StrictSignal[B] = _varSignal | ||
|
||
override protected def defaultDisplayName: String = parent.displayName + displayNameSuffix | ||
} |
33 changes: 33 additions & 0 deletions
33
src/main/scala/com/raquo/airstream/state/LazyDerivedVarSignal.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package com.raquo.airstream.state | ||
|
||
import com.raquo.airstream.core.Protected | ||
import com.raquo.airstream.misc.MapSignal | ||
|
||
import scala.util.Try | ||
|
||
class LazyDerivedVarSignal[I, O]( | ||
parent: Var[I], | ||
zoomIn: I => O, | ||
parentDisplayName: => String | ||
) extends MapSignal[I, O](parent.signal, project = zoomIn, recover = None) with StrictSignal[O] { self => | ||
|
||
// Note that even if owner kills subscription, this signal might remain due to other listeners | ||
// override protected[state] def isStarted: Boolean = super.isStarted | ||
|
||
override protected def defaultDisplayName: String = parentDisplayName + ".signal" | ||
|
||
override def tryNow(): Try[O] = { | ||
val newParentLastUpdateId = Protected.lastUpdateId(parent.signal) | ||
if (newParentLastUpdateId != _parentLastUpdateId) { | ||
// This branch can only run if !isStarted | ||
val nextValue = currentValueFromParent() | ||
updateCurrentValueFromParent(nextValue, newParentLastUpdateId) | ||
nextValue | ||
} else { | ||
super.tryNow() | ||
} | ||
} | ||
|
||
override protected[state] def updateCurrentValueFromParent(nextValue: Try[O], nextParentLastUpdateId: Int): Unit = | ||
super.updateCurrentValueFromParent(nextValue, nextParentLastUpdateId) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.